forked from Dniel97/artemis
Merge branch 'develop' into idac
This commit is contained in:
commit
bfebe69a74
@ -1,6 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
Documenting updates to ARTEMiS, to be updated every time the master branch is pushed to.
|
Documenting updates to ARTEMiS, to be updated every time the master branch is pushed to.
|
||||||
|
|
||||||
|
## 20240318
|
||||||
|
### CXB
|
||||||
|
+ Fixing handle_data_shop_list_detail_request for Sunrise S1
|
||||||
|
|
||||||
## 20240302
|
## 20240302
|
||||||
### SAO
|
### SAO
|
||||||
+ Fixing new profile creation with right heroes and start VP
|
+ Fixing new profile creation with right heroes and start VP
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
"""GekiChu rating tables
|
||||||
|
|
||||||
|
Revision ID: 6a7e8277763b
|
||||||
|
Revises: d8950c7ce2fc
|
||||||
|
Create Date: 2024-03-13 12:18:53.210018
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
from sqlalchemy import Column, Integer, String
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '6a7e8277763b'
|
||||||
|
down_revision = 'd8950c7ce2fc'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
GEKICHU_RATING_TABLE_NAMES = [
|
||||||
|
"chuni_profile_rating",
|
||||||
|
"ongeki_profile_rating",
|
||||||
|
]
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
for table_name in GEKICHU_RATING_TABLE_NAMES:
|
||||||
|
op.create_table(
|
||||||
|
table_name,
|
||||||
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
|
Column("user", Integer, nullable=False),
|
||||||
|
Column("version", Integer, nullable=False),
|
||||||
|
Column("type", String(255), nullable=False),
|
||||||
|
Column("index", Integer, nullable=False),
|
||||||
|
Column("musicId", Integer),
|
||||||
|
Column("difficultId", Integer),
|
||||||
|
Column("romVersionCode", Integer),
|
||||||
|
Column("score", Integer),
|
||||||
|
mysql_charset="utf8mb4",
|
||||||
|
)
|
||||||
|
op.create_foreign_key(
|
||||||
|
None,
|
||||||
|
table_name,
|
||||||
|
"aime_user",
|
||||||
|
["user"],
|
||||||
|
["id"],
|
||||||
|
ondelete="cascade",
|
||||||
|
onupdate="cascade",
|
||||||
|
)
|
||||||
|
op.create_unique_constraint(
|
||||||
|
f"{table_name}_uk",
|
||||||
|
table_name,
|
||||||
|
["user", "version", "type", "index"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
for table_name in GEKICHU_RATING_TABLE_NAMES:
|
||||||
|
op.drop_table(table_name)
|
@ -0,0 +1,68 @@
|
|||||||
|
"""mai2_buddies_support
|
||||||
|
|
||||||
|
Revision ID: 81e44dd6047a
|
||||||
|
Revises: d8950c7ce2fc
|
||||||
|
Create Date: 2024-03-12 19:10:37.063907
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "81e44dd6047a"
|
||||||
|
down_revision = "6a7e8277763b"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.create_table(
|
||||||
|
"mai2_playlog_2p",
|
||||||
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("user", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("userId1", sa.Integer(), nullable=True),
|
||||||
|
sa.Column("userId2", sa.Integer(), nullable=True),
|
||||||
|
sa.Column("userName1", sa.String(length=25), nullable=True),
|
||||||
|
sa.Column("userName2", sa.String(length=25), nullable=True),
|
||||||
|
sa.Column("regionId", sa.Integer(), nullable=True),
|
||||||
|
sa.Column("placeId", sa.Integer(), nullable=True),
|
||||||
|
sa.Column("user2pPlaylogDetailList", sa.JSON(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["user"], ["aime_user.id"], onupdate="cascade", ondelete="cascade"
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
mysql_charset="utf8mb4",
|
||||||
|
)
|
||||||
|
|
||||||
|
op.add_column(
|
||||||
|
"mai2_playlog",
|
||||||
|
sa.Column(
|
||||||
|
"extBool1", sa.Boolean(), nullable=True, server_default=sa.text("NULL")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
op.add_column(
|
||||||
|
"mai2_profile_detail",
|
||||||
|
sa.Column(
|
||||||
|
"renameCredit", sa.Integer(), nullable=True, server_default=sa.text("NULL")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
op.add_column(
|
||||||
|
"mai2_profile_detail",
|
||||||
|
sa.Column(
|
||||||
|
"currentPlayCount",
|
||||||
|
sa.Integer(),
|
||||||
|
nullable=True,
|
||||||
|
server_default=sa.text("NULL"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_table("mai2_playlog_2p")
|
||||||
|
|
||||||
|
op.drop_column("mai2_playlog", "extBool1")
|
||||||
|
op.drop_column("mai2_profile_detail", "renameCredit")
|
||||||
|
op.drop_column("mai2_profile_detail", "currentPlayCount")
|
@ -94,7 +94,7 @@ class ArcadeData(BaseData):
|
|||||||
return None
|
return None
|
||||||
return result.fetchone()
|
return result.fetchone()
|
||||||
|
|
||||||
async def put_machine(
|
async def create_machine(
|
||||||
self,
|
self,
|
||||||
arcade_id: int,
|
arcade_id: int,
|
||||||
serial: str = "",
|
serial: str = "",
|
||||||
@ -102,12 +102,12 @@ class ArcadeData(BaseData):
|
|||||||
game: str = None,
|
game: str = None,
|
||||||
is_cab: bool = False,
|
is_cab: bool = False,
|
||||||
) -> Optional[int]:
|
) -> Optional[int]:
|
||||||
if arcade_id:
|
if not arcade_id:
|
||||||
self.logger.error(f"{__name__ }: Need arcade id!")
|
self.logger.error(f"{__name__ }: Need arcade id!")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
sql = machine.insert().values(
|
sql = machine.insert().values(
|
||||||
arcade=arcade_id, keychip=serial, board=board, game=game, is_cab=is_cab
|
arcade=arcade_id, serial=serial, board=board, game=game, is_cab=is_cab
|
||||||
)
|
)
|
||||||
|
|
||||||
result = await self.execute(sql)
|
result = await self.execute(sql)
|
||||||
@ -148,15 +148,15 @@ class ArcadeData(BaseData):
|
|||||||
return None
|
return None
|
||||||
return result.fetchall()
|
return result.fetchall()
|
||||||
|
|
||||||
async def put_arcade(
|
async def create_arcade(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str = None,
|
||||||
nickname: str = None,
|
nickname: str = None,
|
||||||
country: str = "JPN",
|
country: str = "JPN",
|
||||||
country_id: int = 1,
|
country_id: int = 1,
|
||||||
state: str = "",
|
state: str = "",
|
||||||
city: str = "",
|
city: str = "",
|
||||||
regional_id: int = 1,
|
region_id: int = 1,
|
||||||
) -> Optional[int]:
|
) -> Optional[int]:
|
||||||
if nickname is None:
|
if nickname is None:
|
||||||
nickname = name
|
nickname = name
|
||||||
@ -168,7 +168,7 @@ class ArcadeData(BaseData):
|
|||||||
country_id=country_id,
|
country_id=country_id,
|
||||||
state=state,
|
state=state,
|
||||||
city=city,
|
city=city,
|
||||||
regional_id=regional_id,
|
region_id=region_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
result = await self.execute(sql)
|
result = await self.execute(sql)
|
||||||
@ -206,8 +206,8 @@ class ArcadeData(BaseData):
|
|||||||
return None
|
return None
|
||||||
return result.lastrowid
|
return result.lastrowid
|
||||||
|
|
||||||
async def format_serial(
|
def format_serial( # TODO: Actual serial stuff
|
||||||
self, platform_code: str, platform_rev: int, serial_num: int, append: int = 4152
|
self, platform_code: str, platform_rev: int, serial_num: int, append: int = 8888
|
||||||
) -> str:
|
) -> str:
|
||||||
return f"{platform_code}{platform_rev:02d}A{serial_num:04d}{append:04d}" # 0x41 = A, 0x52 = R
|
return f"{platform_code}{platform_rev:02d}A{serial_num:04d}{append:04d}" # 0x41 = A, 0x52 = R
|
||||||
|
|
||||||
|
120
core/frontend.py
120
core/frontend.py
@ -10,6 +10,9 @@ import bcrypt
|
|||||||
import re
|
import re
|
||||||
import jwt
|
import jwt
|
||||||
import yaml
|
import yaml
|
||||||
|
import secrets
|
||||||
|
import string
|
||||||
|
import random
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
@ -131,6 +134,10 @@ class FrontendServlet():
|
|||||||
Route("/", self.system.render_GET, methods=['GET']),
|
Route("/", self.system.render_GET, methods=['GET']),
|
||||||
Route("/lookup.user", self.system.lookup_user, methods=['GET']),
|
Route("/lookup.user", self.system.lookup_user, methods=['GET']),
|
||||||
Route("/lookup.shop", self.system.lookup_shop, methods=['GET']),
|
Route("/lookup.shop", self.system.lookup_shop, methods=['GET']),
|
||||||
|
Route("/add.user", self.system.add_user, methods=['POST']),
|
||||||
|
Route("/add.card", self.system.add_card, methods=['POST']),
|
||||||
|
Route("/add.shop", self.system.add_shop, methods=['POST']),
|
||||||
|
Route("/add.cab", self.system.add_cab, methods=['POST']),
|
||||||
]),
|
]),
|
||||||
Mount("/shop", routes=[
|
Mount("/shop", routes=[
|
||||||
Route("/", self.arcade.render_GET, methods=['GET']),
|
Route("/", self.arcade.render_GET, methods=['GET']),
|
||||||
@ -551,10 +558,16 @@ class FE_System(FE_Base):
|
|||||||
if not usr_sesh or not self.test_perm_minimum(usr_sesh.permissions, PermissionOffset.USERMOD):
|
if not usr_sesh or not self.test_perm_minimum(usr_sesh.permissions, PermissionOffset.USERMOD):
|
||||||
return RedirectResponse("/gate/", 303)
|
return RedirectResponse("/gate/", 303)
|
||||||
|
|
||||||
|
if request.query_params.get("e", None):
|
||||||
|
err = int(request.query_params.get("e"))
|
||||||
|
else:
|
||||||
|
err = 0
|
||||||
|
|
||||||
return Response(template.render(
|
return Response(template.render(
|
||||||
title=f"{self.core_config.server.name} | System",
|
title=f"{self.core_config.server.name} | System",
|
||||||
sesh=vars(usr_sesh),
|
sesh=vars(usr_sesh),
|
||||||
usrlist=[],
|
usrlist=[],
|
||||||
|
error = err
|
||||||
), media_type="text/html; charset=utf-8")
|
), media_type="text/html; charset=utf-8")
|
||||||
|
|
||||||
async def lookup_user(self, request: Request):
|
async def lookup_user(self, request: Request):
|
||||||
@ -661,6 +674,113 @@ class FE_System(FE_Base):
|
|||||||
shoplist=shoplist,
|
shoplist=shoplist,
|
||||||
), media_type="text/html; charset=utf-8")
|
), media_type="text/html; charset=utf-8")
|
||||||
|
|
||||||
|
async def add_user(self, request: Request):
|
||||||
|
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)
|
||||||
|
|
||||||
|
frm = await request.form()
|
||||||
|
username = frm.get("userName", None)
|
||||||
|
email = frm.get("userEmail", None)
|
||||||
|
perm = frm.get("usrPerm", "1")
|
||||||
|
passwd = "".join(
|
||||||
|
secrets.choice(string.ascii_letters + string.digits) for i in range(20)
|
||||||
|
)
|
||||||
|
hash = bcrypt.hashpw(passwd.encode(), bcrypt.gensalt())
|
||||||
|
|
||||||
|
if not email:
|
||||||
|
return RedirectResponse("/sys/?e=4", 303)
|
||||||
|
|
||||||
|
uid = await self.data.user.create_user(username=username if username else None, email=email, password=hash.decode(), permission=int(perm))
|
||||||
|
return Response(template.render(
|
||||||
|
title=f"{self.core_config.server.name} | System",
|
||||||
|
sesh=vars(usr_sesh),
|
||||||
|
usradd={"id": uid, "username": username, "password": passwd},
|
||||||
|
), media_type="text/html; charset=utf-8")
|
||||||
|
|
||||||
|
async def add_card(self, request: Request):
|
||||||
|
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)
|
||||||
|
|
||||||
|
frm = await request.form()
|
||||||
|
userid = frm.get("cardUsr", None)
|
||||||
|
access_code = frm.get("cardAc", None)
|
||||||
|
idm = frm.get("cardIdm", None)
|
||||||
|
|
||||||
|
if userid is None or access_code is None or not userid.isdigit() or not len(access_code) == 20 or not access_code.isdigit:
|
||||||
|
return RedirectResponse("/sys/?e=4", 303)
|
||||||
|
|
||||||
|
cardid = await self.data.card.create_card(int(userid), access_code)
|
||||||
|
if not cardid:
|
||||||
|
return RedirectResponse("/sys/?e=99", 303)
|
||||||
|
|
||||||
|
if idm is not None:
|
||||||
|
# TODO: save IDM
|
||||||
|
pass
|
||||||
|
|
||||||
|
return Response(template.render(
|
||||||
|
title=f"{self.core_config.server.name} | System",
|
||||||
|
sesh=vars(usr_sesh),
|
||||||
|
cardadd={"id": cardid, "user": userid, "access_code": access_code},
|
||||||
|
), media_type="text/html; charset=utf-8")
|
||||||
|
|
||||||
|
async def add_shop(self, request: Request):
|
||||||
|
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)
|
||||||
|
|
||||||
|
frm = await request.form()
|
||||||
|
name = frm.get("shopName", None)
|
||||||
|
country = frm.get("shopCountry", "JPN")
|
||||||
|
ip = frm.get("shopIp", None)
|
||||||
|
|
||||||
|
acid = await self.data.arcade.create_arcade(name if name else None, name if name else None, country)
|
||||||
|
if not acid:
|
||||||
|
return RedirectResponse("/sys/?e=99", 303)
|
||||||
|
|
||||||
|
if ip:
|
||||||
|
# TODO: set IP
|
||||||
|
pass
|
||||||
|
|
||||||
|
return Response(template.render(
|
||||||
|
title=f"{self.core_config.server.name} | System",
|
||||||
|
sesh=vars(usr_sesh),
|
||||||
|
shopadd={"id": acid},
|
||||||
|
), media_type="text/html; charset=utf-8")
|
||||||
|
|
||||||
|
async def add_cab(self, request: Request):
|
||||||
|
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)
|
||||||
|
|
||||||
|
frm = await request.form()
|
||||||
|
shopid = frm.get("cabShop", None)
|
||||||
|
serial = frm.get("cabSerial", None)
|
||||||
|
game_code = frm.get("cabGame", None)
|
||||||
|
|
||||||
|
if not shopid or not shopid.isdigit():
|
||||||
|
return RedirectResponse("/sys/?e=4", 303)
|
||||||
|
|
||||||
|
if not serial:
|
||||||
|
serial = self.data.arcade.format_serial("A69E", 1, random.randint(1, 9999))
|
||||||
|
|
||||||
|
cab_id = await self.data.arcade.create_machine(int(shopid), serial, None, game_code if game_code else None)
|
||||||
|
|
||||||
|
return Response(template.render(
|
||||||
|
title=f"{self.core_config.server.name} | System",
|
||||||
|
sesh=vars(usr_sesh),
|
||||||
|
cabadd={"id": cab_id, "serial": serial},
|
||||||
|
), media_type="text/html; charset=utf-8")
|
||||||
|
|
||||||
class FE_Arcade(FE_Base):
|
class FE_Arcade(FE_Base):
|
||||||
async def render_GET(self, request: Request):
|
async def render_GET(self, request: Request):
|
||||||
template = self.environment.get_template("core/templates/arcade/index.jinja")
|
template = self.environment.get_template("core/templates/arcade/index.jinja")
|
||||||
|
@ -10,7 +10,7 @@ Cab added successfully
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<ul style="font-size: 20px;">
|
<ul style="font-size: 20px;">
|
||||||
{% for c in arcade.cabs %}
|
{% for c in arcade.cabs %}
|
||||||
<li><a href="/cab/{{ c.id }}">{{ c.serial }} ({{ c.game }})</a> <button class="btn btn-secondary" onclick="prep_edit_form()">Edit</button> <button class="btn-danger btn">Delete</button></li>
|
<li><a href="/cab/{{ c.id }}">{{ c.serial }}</a> ({{ c.game if c.game else "Any" }}) <button class="btn btn-secondary" onclick="prep_edit_form()">Edit</button> <button class="btn-danger btn">Delete</button></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
{% if error is defined %}
|
{% if error is defined %}
|
||||||
{% include "core/templates/widgets/err_banner.jinja" %}
|
{% include "core/templates/widgets/err_banner.jinja" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<h2>Search</h2>
|
||||||
<div class="row" id="rowForm">
|
<div class="row" id="rowForm">
|
||||||
{% if "{:08b}".format(sesh.permissions)[6] == "1" %}
|
{% if "{:08b}".format(sesh.permissions)[6] == "1" %}
|
||||||
<div class="col-sm-6" style="max-width: 25%;">
|
<div class="col-sm-6" style="max-width: 25%;">
|
||||||
@ -21,7 +22,12 @@
|
|||||||
OR
|
OR
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="usrEmail">Email address</label>
|
<label for="usrEmail">Email address</label>
|
||||||
<input type="email" class="form-control" id="usrEmail" name="usrEmail" aria-describedby="emailHelp">
|
<input type="email" class="form-control" id="usrEmail" name="usrEmail">
|
||||||
|
</div>
|
||||||
|
OR
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="usrAc">Access Code</label>
|
||||||
|
<input type="text" class="form-control" id="usrAc" name="usrAc" maxlength="20" placeholder="00000000000000000000">
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<button type="submit" class="btn btn-primary">Search</button>
|
<button type="submit" class="btn btn-primary">Search</button>
|
||||||
@ -63,7 +69,121 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
<h2>Add</h2>
|
||||||
<div class="row" id="rowAdd">
|
<div class="row" id="rowAdd">
|
||||||
|
{% if "{:08b}".format(sesh.permissions)[6] == "1" %}
|
||||||
|
<div class="col-sm-6" style="max-width: 25%;">
|
||||||
|
<form id="usrAdd" name="usrAdd" action="/sys/add.user" class="form-inline" method="POST">
|
||||||
|
<h3>Add User</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="usrName">Username</label>
|
||||||
|
<input type="text" class="form-control" id="usrName" name="usrName">
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="usrEmail">Email address</label>
|
||||||
|
<input type="email" class="form-control" id="usrEmail" name="usrEmail" required>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="usrPerm">Permission Level</label>
|
||||||
|
<input type="number" class="form-control" id="usrPerm" name="usrPerm" value="1">
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="submit" class="btn btn-primary">Add</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6" style="max-width: 25%;">
|
||||||
|
<form id="cardAdd" name="cardAdd" action="/sys/add.card" class="form-inline" method="POST">
|
||||||
|
<h3>Add Card</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="cardUsr">User ID</label>
|
||||||
|
<input type="number" class="form-control" id="cardUsr" name="cardUsr" required>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="cardAc">Access Code</label>
|
||||||
|
<input type="text" class="form-control" id="cardAc" name="cardAc" maxlength="20" placeholder="00000000000000000000" required>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="cardIdm">IDm/Chip ID</label>
|
||||||
|
<input type="text" class="form-control" id="cardIdm" name="cardIdm" disabled>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="submit" class="btn btn-primary">Add</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if "{:08b}".format(sesh.permissions)[5] == "1" %}
|
||||||
|
<div class="col-sm-6" style="max-width: 25%;">
|
||||||
|
<form id="shopAdd" name="shopAdd" action="/sys/add.shop" class="form-inline" method="POST">
|
||||||
|
<h3>Add Shop</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="shopName">Name</label>
|
||||||
|
<input type="text" class="form-control" id="shopName" name="shopName">
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="shopCountry">Country Code</label>
|
||||||
|
<input type="text" class="form-control" id="shopCountry" name="shopCountry" maxlength="3" placeholder="JPN">
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="shopIp">VPN IP</label>
|
||||||
|
<input type="text" class="form-control" id="shopIp" name="shopIp">
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="submit" class="btn btn-primary">Add</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6" style="max-width: 25%;">
|
||||||
|
<form id="cabAdd" name="cabAdd" action="/sys/add.cab" class="form-inline" method="POST">
|
||||||
|
<h3>Add Machine</h3>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="cabShop">Shop ID</label>
|
||||||
|
<input type="number" class="form-control" id="cabShop" name="cabShop" required>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="cabSerial">Serial</label>
|
||||||
|
<input type="text" class="form-control" id="cabSerial" name="cabSerial">
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="cabGame">Game Code</label>
|
||||||
|
<input type="text" class="form-control" id="cabGame" name="cabGame" maxlength="4" placeholder="SXXX">
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<button type="submit" class="btn btn-primary">Add</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="row" id="rowAddResult" style="margin: 10px;">
|
||||||
|
{% if "{:08b}".format(sesh.permissions)[6] == "1" %}
|
||||||
|
<div id="userAddResult" class="col-sm-6" style="max-width: 25%;">
|
||||||
|
{% if usradd is defined %}
|
||||||
|
<pre>Added user {{ usradd.username if usradd.username is not none else "with no name"}} with id {{usradd.id}} and password {{ usradd.password }}</pre>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div id="cardAddResult" class="col-sm-6" style="max-width: 25%;">
|
||||||
|
{% if cardadd is defined %}
|
||||||
|
<pre>Added {{ cardadd.access_code }} with id {{cardadd.id}} to user {{ cardadd.user }}</pre>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if "{:08b}".format(sesh.permissions)[5] == "1" %}
|
||||||
|
<div id="shopAddResult" class="col-sm-6" style="max-width: 25%;">
|
||||||
|
{% if shopadd is defined %}
|
||||||
|
<pre>Added Shop {{ shopadd.id }}</pre></a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div id="cabAddResult" class="col-sm-6" style="max-width: 25%;">
|
||||||
|
{% if cabadd is defined %}
|
||||||
|
<pre>Added Machine {{ cabadd.id }} with serial {{ cabadd.serial }}</pre></a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
@ -194,6 +194,7 @@ Config file is located in `config/cxb.yaml`.
|
|||||||
| SDEZ | 18 | maimai DX UNiVERSE PLUS |
|
| SDEZ | 18 | maimai DX UNiVERSE PLUS |
|
||||||
| SDEZ | 19 | maimai DX FESTiVAL |
|
| SDEZ | 19 | maimai DX FESTiVAL |
|
||||||
| SDEZ | 20 | maimai DX FESTiVAL PLUS |
|
| SDEZ | 20 | maimai DX FESTiVAL PLUS |
|
||||||
|
| SDEZ | 21 | maimai DX BUDDiES |
|
||||||
|
|
||||||
### Importer
|
### Importer
|
||||||
|
|
||||||
@ -408,6 +409,7 @@ After that, on next login the present should be received (or whenever it suppose
|
|||||||
* UNiVERSE PLUS: Yes
|
* UNiVERSE PLUS: Yes
|
||||||
* FESTiVAL: Yes (added in A031)
|
* FESTiVAL: Yes (added in A031)
|
||||||
* FESTiVAL PLUS: Yes (added in A035)
|
* FESTiVAL PLUS: Yes (added in A035)
|
||||||
|
* BUDDiES: Yes (added in A039)
|
||||||
* O.N.G.E.K.I. bright MEMORY: Yes
|
* O.N.G.E.K.I. bright MEMORY: Yes
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ Games listed below have been tested and confirmed working. Only game versions ol
|
|||||||
+ UNiVERSE PLUS
|
+ UNiVERSE PLUS
|
||||||
+ FESTiVAL
|
+ FESTiVAL
|
||||||
+ FESTiVAL PLUS
|
+ FESTiVAL PLUS
|
||||||
|
+ BUDDiES
|
||||||
|
|
||||||
+ O.N.G.E.K.I.
|
+ O.N.G.E.K.I.
|
||||||
+ SUMMER
|
+ SUMMER
|
||||||
|
@ -1,23 +1,24 @@
|
|||||||
mypy
|
mypy
|
||||||
wheel
|
wheel
|
||||||
pytz
|
pytz
|
||||||
pyyaml
|
pyyaml
|
||||||
sqlalchemy==1.4.46
|
sqlalchemy==1.4.46
|
||||||
mysqlclient
|
mysqlclient
|
||||||
pyopenssl
|
pyopenssl
|
||||||
service_identity
|
service_identity
|
||||||
PyCryptodome
|
PyCryptodome
|
||||||
inflection
|
inflection
|
||||||
coloredlogs
|
coloredlogs
|
||||||
pylibmc; platform_system != "Windows"
|
pylibmc; platform_system != "Windows"
|
||||||
wacky
|
wacky
|
||||||
bcrypt
|
bcrypt
|
||||||
jinja2
|
jinja2
|
||||||
protobuf
|
protobuf
|
||||||
pillow
|
pillow
|
||||||
pyjwt==2.8.0
|
pyjwt==2.8.0
|
||||||
websockets
|
websockets
|
||||||
starlette
|
starlette
|
||||||
asyncio
|
asyncio
|
||||||
uvicorn
|
uvicorn
|
||||||
alembic
|
alembic
|
||||||
|
python-multipart
|
@ -925,6 +925,17 @@ class ChuniBase:
|
|||||||
for rp in upsert["userRecentPlayerList"]:
|
for rp in upsert["userRecentPlayerList"]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
for rating_type in {"userRatingBaseList", "userRatingBaseHotList", "userRatingBaseNextList"}:
|
||||||
|
if rating_type not in upsert:
|
||||||
|
continue
|
||||||
|
|
||||||
|
await self.data.profile.put_profile_rating(
|
||||||
|
user_id,
|
||||||
|
self.version,
|
||||||
|
rating_type,
|
||||||
|
upsert[rating_type],
|
||||||
|
)
|
||||||
|
|
||||||
return {"returnCode": "1"}
|
return {"returnCode": "1"}
|
||||||
|
|
||||||
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
|
async def handle_upsert_user_chargelog_api_request(self, data: Dict) -> Dict:
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
from sqlalchemy import Table, Column, UniqueConstraint, and_
|
||||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger
|
from sqlalchemy.types import Integer, String, Boolean, JSON, BigInteger
|
||||||
from sqlalchemy.engine.base import Connection
|
|
||||||
from sqlalchemy.schema import ForeignKey
|
from sqlalchemy.schema import ForeignKey
|
||||||
from sqlalchemy.engine import Row
|
from sqlalchemy.engine import Row
|
||||||
from sqlalchemy.sql import func, select
|
from sqlalchemy.sql import select, delete
|
||||||
from sqlalchemy.dialects.mysql import insert
|
from sqlalchemy.dialects.mysql import insert
|
||||||
|
|
||||||
from core.data.schema import BaseData, metadata
|
from core.data.schema import BaseData, metadata
|
||||||
@ -393,6 +392,26 @@ team = Table(
|
|||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rating = Table(
|
||||||
|
"chuni_profile_rating",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
|
Column(
|
||||||
|
"user",
|
||||||
|
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
Column("version", Integer, nullable=False),
|
||||||
|
Column("type", String(255), nullable=False),
|
||||||
|
Column("index", Integer, nullable=False),
|
||||||
|
Column("musicId", Integer),
|
||||||
|
Column("difficultId", Integer),
|
||||||
|
Column("romVersionCode", Integer),
|
||||||
|
Column("score", Integer),
|
||||||
|
UniqueConstraint("user", "version", "type", "index", name="chuni_profile_rating_best_uk"),
|
||||||
|
mysql_charset="utf8mb4",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ChuniProfileData(BaseData):
|
class ChuniProfileData(BaseData):
|
||||||
async def update_name(self, user_id: int, new_name: str) -> bool:
|
async def update_name(self, user_id: int, new_name: str) -> bool:
|
||||||
@ -714,3 +733,27 @@ class ChuniProfileData(BaseData):
|
|||||||
return {
|
return {
|
||||||
"total_play_count": total_play_count
|
"total_play_count": total_play_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def put_profile_rating(
|
||||||
|
self,
|
||||||
|
aime_id: int,
|
||||||
|
version: int,
|
||||||
|
rating_type: str,
|
||||||
|
rating_data: List[Dict],
|
||||||
|
):
|
||||||
|
inserted_values = [
|
||||||
|
{"user": aime_id, "version": version, "type": rating_type, "index": i, **x}
|
||||||
|
for (i, x) in enumerate(rating_data)
|
||||||
|
]
|
||||||
|
sql = insert(rating).values(inserted_values)
|
||||||
|
update_dict = {x.name: x for x in sql.inserted if x.name != "id"}
|
||||||
|
sql = sql.on_duplicate_key_update(**update_dict)
|
||||||
|
result = await self.execute(sql)
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
self.logger.warn(
|
||||||
|
f"put_profile_rating: Could not insert {rating_type}, aime_id: {aime_id}",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
return result.lastrowid
|
||||||
|
@ -206,6 +206,7 @@ class CardMakerReader(BaseReader):
|
|||||||
"1.25": Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS,
|
"1.25": Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS,
|
||||||
"1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL,
|
"1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL,
|
||||||
"1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS,
|
"1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS,
|
||||||
|
"1.40": Mai2Constants.VER_MAIMAI_DX_BUDDIES,
|
||||||
}
|
}
|
||||||
|
|
||||||
for root, dirs, files in os.walk(base_dir):
|
for root, dirs, files in os.walk(base_dir):
|
||||||
@ -225,12 +226,6 @@ class CardMakerReader(BaseReader):
|
|||||||
True if troot.find("disable").text == "false" else False
|
True if troot.find("disable").text == "false" else False
|
||||||
)
|
)
|
||||||
|
|
||||||
# check if a date is part of the name and disable the
|
|
||||||
# card if it is
|
|
||||||
enabled = (
|
|
||||||
False if re.search(r"\d{2}/\d{2}/\d{2}", name) else enabled
|
|
||||||
)
|
|
||||||
|
|
||||||
await self.mai2_data.static.put_card(
|
await self.mai2_data.static.put_card(
|
||||||
version, card_id, name, enabled=enabled
|
version, card_id, name, enabled=enabled
|
||||||
)
|
)
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
saleID.,<2C>J<EFBFBD>n<EFBFBD><6E>,<2C>I<EFBFBD><49><EFBFBD><EFBFBD>,ShopID,Price,
|
|
||||||
0,1411696799,1443236400,0,7000,
|
|
||||||
1,1411783199,1443322800,1,7000,
|
|
|
@ -1,4 +0,0 @@
|
|||||||
shopID.,整理用No.,Ver.,出現フラグ,出現フラグ参照ID,条件,出現時間,消滅時間,ItemCode,価格,表示タイプ,Text,Type,Value(op),Value,対象曲,Difficulty(op),Difficulty,Level(op),Level,Grade(Op),Grade,GaugeType(op),GaugeType,HS(op)i,HS,APP,DAP,F-V,F-H,FullCombo,Combo(op),Combo,ClearRate(op),ClearRate,プレイ日,地域,
|
|
||||||
3000,1,1.00.00,1,-1,-,1411697520.0288,1443233520.0288,skb0000,10,1,MASTER以上の2曲S+以上フルコンボクリアする。,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
3001,2,1.00.00,1,-1,-,1411697520.0288,1443233520.0288,skb0001,10,1,Next Frontier (Master)をクリア,0,-1,-1,bleeze,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
3002,3,1.00.00,1,-1,-,1412103600.0288,1443639598.992,skb0002,10,2,Master以上を1曲をS+以上でクリアする。,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,1,
|
|
|
@ -1,11 +0,0 @@
|
|||||||
shopID.,<2C><><EFBFBD><EFBFBD><EFBFBD>pNo.,Ver.,<2C>o<EFBFBD><6F><EFBFBD>t<EFBFBD><74><EFBFBD>O,<2C>o<EFBFBD><6F><EFBFBD>t<EFBFBD><74><EFBFBD>O<EFBFBD>Q<EFBFBD><51>ID,<2C><><EFBFBD><EFBFBD>,<2C>o<EFBFBD><6F><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD>Ŏ<EFBFBD><C58E><EFBFBD>,ItemCode,<2C><><EFBFBD>i,<2C>\<5C><><EFBFBD>^<5E>C<EFBFBD>v,Text,Type,Value(op),Value,<2C>Ώۋ<CE8F>,Difficulty(op),Difficulty,Level(op),Level,Grade(Op),Grade,GaugeType(op),GaugeType,HS(op)i,HS,APP,DAP,F-V,F-H,FullCombo,Combo(op),Combo,ClearRate(op),ClearRate,<2C>v<EFBFBD><76><EFBFBD>C<EFBFBD><43>,<2C>n<EFBFBD><6E>,
|
|
||||||
5000,1,10000,1,-1,-,1411697520,1443233520,ske0000,10,1,MASTER<45>ȏ<EFBFBD><C88F>2<EFBFBD><32>S+<2B>ȏ<EFBFBD>t<EFBFBD><74><EFBFBD>R<EFBFBD><52><EFBFBD>{<7B>N<EFBFBD><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
5001,2,10000,1,-1,-,1411697520,1443233520,ske0001,10,1,Next Frontier (Master<65>j<EFBFBD><6A><EFBFBD>N<EFBFBD><4E><EFBFBD>A,0,-1,-1,megaro,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
5002,3,10000,1,-1,-,1412103600,1443639598,ske0002,10,2,Master<65>ȏ<EFBFBD><C88F>1<EFBFBD>Ȃ<EFBFBD>S+<2B>ȏ<EFBFBD>ŃN<C583><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,1,
|
|
||||||
5003,4,10000,1,-1,-,1412103600,1443639598,ske0003,10,0,Master<65>ȏ<EFBFBD><C88F>1<EFBFBD>Ȃ<EFBFBD>S+<2B>ȏ<EFBFBD>ŃN<C583><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
5004,5,10000,1,-1,-,1412103600,1443639598,ske0004,10,2,2<>ȃN<C883><4E><EFBFBD>A,1,1,2,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
5005,5,10000,1,-1,-,1412103600,1443639598,ske0005,10,2,3<>ȃN<C883><4E><EFBFBD>A,1,1,3,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
5006,5,10000,1,-1,-,1412103600,1443639598,ske0006,10,2,4<>ȃN<C883><4E><EFBFBD>A,1,1,4,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
5007,5,10000,1,-1,-,1412103600,1443639598,ske0007,10,2,5<>ȃN<C883><4E><EFBFBD>A,1,1,5,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
5008,5,10000,1,-1,-,1412103600,1443639598,ske0008,10,2,6<>ȃN<C883><4E><EFBFBD>A,1,1,6,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
5009,5,10000,1,-1,-,1412103600,1443639598,ske0009,10,2,7<>ȃN<C883><4E><EFBFBD>A,1,1,7,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
|
@ -1,6 +0,0 @@
|
|||||||
shopID.,<2C><><EFBFBD><EFBFBD><EFBFBD>pNo.,Ver.,<2C>o<EFBFBD><6F><EFBFBD>t<EFBFBD><74><EFBFBD>O,<2C>o<EFBFBD><6F><EFBFBD>t<EFBFBD><74><EFBFBD>O<EFBFBD>Q<EFBFBD><51>ID,<2C><><EFBFBD><EFBFBD>,<2C>o<EFBFBD><6F><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD>Ŏ<EFBFBD><C58E><EFBFBD>,ItemCode,<2C><><EFBFBD>i,<2C>\<5C><><EFBFBD>^<5E>C<EFBFBD>v,Text,Type,Value(op),Value,<2C>Ώۋ<CE8F>,Difficulty(op),Difficulty,Level(op),Level,Grade(Op),Grade,GaugeType(op),GaugeType,HS(op)i,HS,APP,DAP,F-V,F-H,FullCombo,Combo(op),Combo,ClearRate(op),ClearRate,<2C>v<EFBFBD><76><EFBFBD>C<EFBFBD><43>,<2C>n<EFBFBD><6E>,
|
|
||||||
4000,1,10000,1,-1,-,1411697520,4096483201,skt0000,10,1,MASTER<45>ȏ<EFBFBD><C88F>2<EFBFBD><32>S+<2B>ȏ<EFBFBD>t<EFBFBD><74><EFBFBD>R<EFBFBD><52><EFBFBD>{<7B>N<EFBFBD><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
4001,2,10000,1,-1,-,1411697520,4096483201,skt0001,10,1,Next Frontier (Master<65>j<EFBFBD><6A><EFBFBD>N<EFBFBD><4E><EFBFBD>A,0,-1,-1,megaro,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
4002,3,10000,1,-1,-,1412103600,4096483201,skt0002,10,2,Master<65>ȏ<EFBFBD><C88F>1<EFBFBD>Ȃ<EFBFBD>S+<2B>ȏ<EFBFBD>ŃN<C583><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,1,
|
|
||||||
4003,4,10000,1,-1,-,1412103600,4096483201,skt0003,10,0,Master<65>ȏ<EFBFBD><C88F>1<EFBFBD>Ȃ<EFBFBD>S+<2B>ȏ<EFBFBD>ŃN<C583><4E><EFBFBD>A<EFBFBD><41><EFBFBD><EFBFBD>B,1,1,1,-,1,2,-1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
||||||
4004,5,10000,1,-1,-,1412103600,4096483201,skt0004,10,2,aaaaaaaaaaaaaaaaa,1,1,20,-,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,,,
|
|
|
@ -73,42 +73,6 @@ class CxbRevSunriseS1(CxbBase):
|
|||||||
for line in lines:
|
for line in lines:
|
||||||
ret_str += f"{line[:-1]}\r\n"
|
ret_str += f"{line[:-1]}\r\n"
|
||||||
|
|
||||||
# ShopListSale load
|
|
||||||
ret_str += "\r\n#ShopListSale\r\n"
|
|
||||||
with open(
|
|
||||||
r"titles/cxb/data/rss1/Shop/ShopList_Sale.csv", encoding="shift-jis"
|
|
||||||
) as shop:
|
|
||||||
lines = shop.readlines()
|
|
||||||
for line in lines:
|
|
||||||
ret_str += f"{line[:-1]}\r\n"
|
|
||||||
|
|
||||||
# ShopListSkinBg load
|
|
||||||
ret_str += "\r\n#ShopListSkinBg\r\n"
|
|
||||||
with open(
|
|
||||||
r"titles/cxb/data/rss1/Shop/ShopList_SkinBg.csv", encoding="shift-jis"
|
|
||||||
) as shop:
|
|
||||||
lines = shop.readlines()
|
|
||||||
for line in lines:
|
|
||||||
ret_str += f"{line[:-1]}\r\n"
|
|
||||||
|
|
||||||
# ShopListSkinEffect load
|
|
||||||
ret_str += "\r\n#ShopListSkinEffect\r\n"
|
|
||||||
with open(
|
|
||||||
r"titles/cxb/data/rss1/Shop/ShopList_SkinEffect.csv", encoding="shift-jis"
|
|
||||||
) as shop:
|
|
||||||
lines = shop.readlines()
|
|
||||||
for line in lines:
|
|
||||||
ret_str += f"{line[:-1]}\r\n"
|
|
||||||
|
|
||||||
# ShopListSkinNotes load
|
|
||||||
ret_str += "\r\n#ShopListSkinNotes\r\n"
|
|
||||||
with open(
|
|
||||||
r"titles/cxb/data/rss1/Shop/ShopList_SkinNotes.csv", encoding="shift-jis"
|
|
||||||
) as shop:
|
|
||||||
lines = shop.readlines()
|
|
||||||
for line in lines:
|
|
||||||
ret_str += f"{line[:-1]}\r\n"
|
|
||||||
|
|
||||||
# ShopListTitle load
|
# ShopListTitle load
|
||||||
ret_str += "\r\n#ShopListTitle\r\n"
|
ret_str += "\r\n#ShopListTitle\r\n"
|
||||||
with open(
|
with open(
|
||||||
|
32
titles/mai2/buddies.py
Normal file
32
titles/mai2/buddies.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from core.config import CoreConfig
|
||||||
|
from titles.mai2.festivalplus import Mai2FestivalPlus
|
||||||
|
from titles.mai2.const import Mai2Constants
|
||||||
|
from titles.mai2.config import Mai2Config
|
||||||
|
|
||||||
|
|
||||||
|
class Mai2Buddies(Mai2FestivalPlus):
|
||||||
|
def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None:
|
||||||
|
super().__init__(cfg, game_cfg)
|
||||||
|
self.version = Mai2Constants.VER_MAIMAI_DX_BUDDIES
|
||||||
|
|
||||||
|
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.40.00"
|
||||||
|
return user_data
|
||||||
|
|
||||||
|
async def handle_get_user_new_item_api_request(self, data: Dict) -> Dict:
|
||||||
|
# TODO: Added in 1.41, implement this?
|
||||||
|
user_id = data["userId"]
|
||||||
|
version = data.get("version", 1041000)
|
||||||
|
user_playlog_list = data.get("userPlaylogList", [])
|
||||||
|
|
||||||
|
return {
|
||||||
|
"userId": user_id,
|
||||||
|
"itemKind": -1,
|
||||||
|
"itemId": -1,
|
||||||
|
}
|
||||||
|
|
@ -53,6 +53,7 @@ class Mai2Constants:
|
|||||||
VER_MAIMAI_DX_UNIVERSE_PLUS = 18
|
VER_MAIMAI_DX_UNIVERSE_PLUS = 18
|
||||||
VER_MAIMAI_DX_FESTIVAL = 19
|
VER_MAIMAI_DX_FESTIVAL = 19
|
||||||
VER_MAIMAI_DX_FESTIVAL_PLUS = 20
|
VER_MAIMAI_DX_FESTIVAL_PLUS = 20
|
||||||
|
VER_MAIMAI_DX_BUDDIES = 21
|
||||||
|
|
||||||
VERSION_STRING = (
|
VERSION_STRING = (
|
||||||
"maimai",
|
"maimai",
|
||||||
@ -76,6 +77,7 @@ class Mai2Constants:
|
|||||||
"maimai DX UNiVERSE PLUS",
|
"maimai DX UNiVERSE PLUS",
|
||||||
"maimai DX FESTiVAL",
|
"maimai DX FESTiVAL",
|
||||||
"maimai DX FESTiVAL PLUS",
|
"maimai DX FESTiVAL PLUS",
|
||||||
|
"maimai DX BUDDiES"
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -212,6 +212,9 @@ class Mai2DX(Mai2Base):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
await self.data.item.put_friend_season_ranking(user_id, fsr)
|
await self.data.item.put_friend_season_ranking(user_id, fsr)
|
||||||
|
|
||||||
|
if "user2pPlaylog" in upsert:
|
||||||
|
await self.data.score.put_playlog_2p(user_id, upsert["user2pPlaylog"])
|
||||||
|
|
||||||
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
|
return {"returnCode": 1, "apiName": "UpsertUserAllApi"}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ from .universe import Mai2Universe
|
|||||||
from .universeplus import Mai2UniversePlus
|
from .universeplus import Mai2UniversePlus
|
||||||
from .festival import Mai2Festival
|
from .festival import Mai2Festival
|
||||||
from .festivalplus import Mai2FestivalPlus
|
from .festivalplus import Mai2FestivalPlus
|
||||||
|
from .buddies import Mai2Buddies
|
||||||
|
|
||||||
|
|
||||||
class Mai2Servlet(BaseServlet):
|
class Mai2Servlet(BaseServlet):
|
||||||
@ -58,6 +59,7 @@ class Mai2Servlet(BaseServlet):
|
|||||||
Mai2UniversePlus,
|
Mai2UniversePlus,
|
||||||
Mai2Festival,
|
Mai2Festival,
|
||||||
Mai2FestivalPlus,
|
Mai2FestivalPlus,
|
||||||
|
Mai2Buddies
|
||||||
]
|
]
|
||||||
|
|
||||||
self.logger = logging.getLogger("mai2")
|
self.logger = logging.getLogger("mai2")
|
||||||
@ -257,8 +259,10 @@ class Mai2Servlet(BaseServlet):
|
|||||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
|
internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS
|
||||||
elif version >= 130 and version < 135: # FESTiVAL
|
elif version >= 130 and version < 135: # FESTiVAL
|
||||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
|
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL
|
||||||
elif version >= 135: # FESTiVAL PLUS
|
elif version >= 135 and version < 140: # FESTiVAL PLUS
|
||||||
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
|
internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS
|
||||||
|
elif version >= 140: # BUDDiES
|
||||||
|
internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES
|
||||||
|
|
||||||
if (
|
if (
|
||||||
request.headers.get("Mai-Encoding") is not None
|
request.headers.get("Mai-Encoding") is not None
|
||||||
|
@ -40,6 +40,8 @@ detail = Table(
|
|||||||
Column("charaLockSlot", JSON),
|
Column("charaLockSlot", JSON),
|
||||||
Column("contentBit", BigInteger),
|
Column("contentBit", BigInteger),
|
||||||
Column("playCount", Integer),
|
Column("playCount", Integer),
|
||||||
|
Column("currentPlayCount", Integer), # new with buddies
|
||||||
|
Column("renameCredit", Integer), # new with buddies
|
||||||
Column("mapStock", Integer), # new with fes+
|
Column("mapStock", Integer), # new with fes+
|
||||||
Column("eventWatchedDate", String(25)),
|
Column("eventWatchedDate", String(25)),
|
||||||
Column("lastGameId", String(25)),
|
Column("lastGameId", String(25)),
|
||||||
|
@ -145,11 +145,34 @@ playlog = Table(
|
|||||||
Column("isNewFree", Boolean),
|
Column("isNewFree", Boolean),
|
||||||
Column("extNum1", Integer),
|
Column("extNum1", Integer),
|
||||||
Column("extNum2", Integer),
|
Column("extNum2", Integer),
|
||||||
Column("extNum4", Integer, server_default="0"),
|
Column("extNum4", Integer),
|
||||||
|
Column("extBool1", Boolean), # new with buddies
|
||||||
Column("trialPlayAchievement", Integer),
|
Column("trialPlayAchievement", Integer),
|
||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# new with buddies
|
||||||
|
playlog_2p = Table(
|
||||||
|
"mai2_playlog_2p",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
|
Column(
|
||||||
|
"user",
|
||||||
|
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
# TODO: ForeignKey to aime_user?
|
||||||
|
Column("userId1", Integer),
|
||||||
|
Column("userId2", Integer),
|
||||||
|
# TODO: ForeignKey to mai2_profile_detail?
|
||||||
|
Column("userName1", String(25)),
|
||||||
|
Column("userName2", String(25)),
|
||||||
|
Column("regionId", Integer),
|
||||||
|
Column("placeId", Integer),
|
||||||
|
Column("user2pPlaylogDetailList", JSON),
|
||||||
|
mysql_charset="utf8mb4",
|
||||||
|
)
|
||||||
|
|
||||||
course = Table(
|
course = Table(
|
||||||
"mai2_score_course",
|
"mai2_score_course",
|
||||||
metadata,
|
metadata,
|
||||||
@ -343,6 +366,18 @@ class Mai2ScoreData(BaseData):
|
|||||||
self.logger.error(f"put_playlog: Failed to insert! user_id {user_id} is_dx {is_dx}")
|
self.logger.error(f"put_playlog: Failed to insert! user_id {user_id} is_dx {is_dx}")
|
||||||
return None
|
return None
|
||||||
return result.lastrowid
|
return result.lastrowid
|
||||||
|
|
||||||
|
async def put_playlog_2p(self, user_id: int, playlog_2p_data: Dict) -> Optional[int]:
|
||||||
|
playlog_2p_data["user"] = user_id
|
||||||
|
sql = insert(playlog_2p).values(**playlog_2p_data)
|
||||||
|
|
||||||
|
conflict = sql.on_duplicate_key_update(**playlog_2p_data)
|
||||||
|
|
||||||
|
result = await self.execute(conflict)
|
||||||
|
if result is None:
|
||||||
|
self.logger.error(f"put_playlog_2p: Failed to insert! user_id {user_id}")
|
||||||
|
return None
|
||||||
|
return result.lastrowid
|
||||||
|
|
||||||
async 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
|
course_data["user"] = user_id
|
||||||
|
@ -1067,6 +1067,24 @@ class OngekiBase:
|
|||||||
if "userKopList" in upsert:
|
if "userKopList" in upsert:
|
||||||
for x in upsert["userKopList"]:
|
for x in upsert["userKopList"]:
|
||||||
await self.data.profile.put_kop(user_id, x)
|
await self.data.profile.put_kop(user_id, x)
|
||||||
|
|
||||||
|
for rating_type in {
|
||||||
|
"userRatingBaseBestList",
|
||||||
|
"userRatingBaseBestNewList",
|
||||||
|
"userRatingBaseHotList",
|
||||||
|
"userRatingBaseNextList",
|
||||||
|
"userRatingBaseNextNewList",
|
||||||
|
"userRatingBaseHotNextList",
|
||||||
|
}:
|
||||||
|
if rating_type not in upsert:
|
||||||
|
continue
|
||||||
|
|
||||||
|
await self.data.profile.put_profile_rating(
|
||||||
|
user_id,
|
||||||
|
self.version,
|
||||||
|
rating_type,
|
||||||
|
upsert[rating_type],
|
||||||
|
)
|
||||||
|
|
||||||
return {"returnCode": 1, "apiName": "upsertUserAll"}
|
return {"returnCode": 1, "apiName": "upsertUserAll"}
|
||||||
|
|
||||||
|
@ -246,6 +246,26 @@ rival = Table(
|
|||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rating = Table(
|
||||||
|
"ongeki_profile_rating",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, primary_key=True, nullable=False),
|
||||||
|
Column(
|
||||||
|
"user",
|
||||||
|
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
Column("version", Integer, nullable=False),
|
||||||
|
Column("type", String(255), nullable=False),
|
||||||
|
Column("index", Integer, nullable=False),
|
||||||
|
Column("musicId", Integer),
|
||||||
|
Column("difficultId", Integer),
|
||||||
|
Column("romVersionCode", Integer),
|
||||||
|
Column("score", Integer),
|
||||||
|
UniqueConstraint("user", "version", "type", "index", name="ongeki_profile_rating_best_uk"),
|
||||||
|
mysql_charset="utf8mb4",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OngekiProfileData(BaseData):
|
class OngekiProfileData(BaseData):
|
||||||
def __init__(self, cfg: CoreConfig, conn: Connection) -> None:
|
def __init__(self, cfg: CoreConfig, conn: Connection) -> None:
|
||||||
@ -508,10 +528,35 @@ class OngekiProfileData(BaseData):
|
|||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
return result.lastrowid
|
return result.lastrowid
|
||||||
|
|
||||||
async 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)
|
sql = delete(rival).where(rival.c.user==aime_id, rival.c.rivalUserId==rival_id)
|
||||||
result = await self.execute(sql)
|
result = await self.execute(sql)
|
||||||
if result is None:
|
if result is None:
|
||||||
self.logger.error(f"delete_rival: failed to delete! aime_id: {aime_id}, rival_id: {rival_id}")
|
self.logger.error(f"delete_rival: failed to delete! aime_id: {aime_id}, rival_id: {rival_id}")
|
||||||
else:
|
else:
|
||||||
return result.rowcount
|
return result.rowcount
|
||||||
|
|
||||||
|
async def put_profile_rating(
|
||||||
|
self,
|
||||||
|
aime_id: int,
|
||||||
|
version: int,
|
||||||
|
rating_type: str,
|
||||||
|
rating_data: List[Dict],
|
||||||
|
):
|
||||||
|
inserted_values = [
|
||||||
|
{"user": aime_id, "version": version, "type": rating_type, "index": i, **x}
|
||||||
|
for (i, x) in enumerate(rating_data)
|
||||||
|
]
|
||||||
|
sql = insert(rating).values(inserted_values)
|
||||||
|
update_dict = {x.name: x for x in sql.inserted if x.name != "id"}
|
||||||
|
sql = sql.on_duplicate_key_update(**update_dict)
|
||||||
|
result = await self.execute(sql)
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
self.logger.warn(
|
||||||
|
f"put_profile_rating_{rating_type}: Could not insert rating entries, aime_id: {aime_id}",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
return result.lastrowid
|
||||||
|
@ -11,25 +11,24 @@ from titles.wacca.handlers import *
|
|||||||
|
|
||||||
|
|
||||||
class WaccaS(WaccaBase):
|
class WaccaS(WaccaBase):
|
||||||
allowed_stages = [
|
|
||||||
(1513, 13),
|
|
||||||
(1512, 12),
|
|
||||||
(1511, 11),
|
|
||||||
(1510, 10),
|
|
||||||
(1509, 9),
|
|
||||||
(1508, 8),
|
|
||||||
(1507, 7),
|
|
||||||
(1506, 6),
|
|
||||||
(1505, 5),
|
|
||||||
(1514, 4),
|
|
||||||
(1513, 3),
|
|
||||||
(1512, 2),
|
|
||||||
(1511, 1),
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, cfg: CoreConfig, game_cfg: WaccaConfig) -> None:
|
def __init__(self, cfg: CoreConfig, game_cfg: WaccaConfig) -> None:
|
||||||
super().__init__(cfg, game_cfg)
|
super().__init__(cfg, game_cfg)
|
||||||
self.version = WaccaConstants.VER_WACCA_S
|
self.version = WaccaConstants.VER_WACCA_S
|
||||||
|
self.allowed_stages = [
|
||||||
|
(1513, 13),
|
||||||
|
(1512, 12),
|
||||||
|
(1511, 11),
|
||||||
|
(1510, 10),
|
||||||
|
(1509, 9),
|
||||||
|
(1508, 8),
|
||||||
|
(1507, 7),
|
||||||
|
(1506, 6),
|
||||||
|
(1505, 5),
|
||||||
|
(1504, 4),
|
||||||
|
(1503, 3),
|
||||||
|
(1502, 2),
|
||||||
|
(1501, 1),
|
||||||
|
]
|
||||||
|
|
||||||
async def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
async def handle_advertise_GetNews_request(self, data: Dict) -> Dict:
|
||||||
resp = GetNewsResponseV2()
|
resp = GetNewsResponseV2()
|
||||||
|
Loading…
Reference in New Issue
Block a user