mirror of
https://gitea.tendokyu.moe/Dniel97/artemis.git
synced 2026-09-22 22:28:25 +03:00
Initial D THE ARCADE Season 3 support (#1)
## Changes: - [x] Added database upgrade script for season 3 tables - [x] Completly implement season 3 - [x] Teams are fully functional as intended - [x] Time Release is properly updated for season 3 (v2.10.00) - [x] New accounts can be created and saved in db - [x] Old 2eason 2 accounts can be transfered to season 3 - [x] Season independed config has been added inclusive version specific configs Co-authored-by: puniru <puniru@posteo.jp> Reviewed-on: https://gitea.tendokyu.moe/Dniel97/artemis-IDAC/pulls/1 Co-authored-by: Dniel97 <Dniel97@noreply.gitea.tendokyu.moe> Co-committed-by: Dniel97 <Dniel97@noreply.gitea.tendokyu.moe>
This commit is contained in:
committed by
Dniel97
co-authored by
puniru
parent
efc798a28a
commit
3ac8762868
@@ -0,0 +1,308 @@
|
||||
"""IDAC Season 3
|
||||
|
||||
|
||||
Revision ID: 71fad2543ff7
|
||||
Revises: 49c295e89cd4
|
||||
Create Date: 2024-12-29 21:15:36.621390
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "71fad2543ff7"
|
||||
down_revision = "bdf710616ba4"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
PROFILE_STOCK_COLUMNS = [
|
||||
"avatar_face_list",
|
||||
"avatar_face_new_list",
|
||||
"avatar_eye_list",
|
||||
"avatar_eye_new_list",
|
||||
"avatar_hair_list",
|
||||
"avatar_hair_new_list",
|
||||
"avatar_body_list",
|
||||
"avatar_body_new_list",
|
||||
"avatar_mouth_list",
|
||||
"avatar_mouth_new_list",
|
||||
"avatar_glasses_list",
|
||||
"avatar_glasses_new_list",
|
||||
"avatar_face_accessory_list",
|
||||
"avatar_face_accessory_new_list",
|
||||
"avatar_body_accessory_list",
|
||||
"avatar_body_accessory_new_list",
|
||||
"avatar_behind_list",
|
||||
"avatar_behind_new_list",
|
||||
"avatar_bg_list",
|
||||
"avatar_bg_new_list",
|
||||
"avatar_effect_list",
|
||||
"avatar_effect_new_list",
|
||||
"avatar_special_list",
|
||||
"avatar_special_new_list",
|
||||
"stamp_list",
|
||||
"stamp_new_list",
|
||||
"keyholder_list",
|
||||
"keyholder_new_list",
|
||||
"papercup_list",
|
||||
"papercup_new_list",
|
||||
"tachometer_list",
|
||||
"tachometer_new_list",
|
||||
"aura_list",
|
||||
"aura_new_list",
|
||||
"aura_color_list",
|
||||
"aura_color_new_list",
|
||||
"aura_line_list",
|
||||
"aura_line_new_list",
|
||||
"bgm_list",
|
||||
"bgm_new_list",
|
||||
"dx_color_list",
|
||||
"dx_color_new_list",
|
||||
"start_menu_bg_list",
|
||||
"start_menu_bg_new_list",
|
||||
"under_neon_list",
|
||||
]
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"idac_team_info",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("version", sa.Integer(), nullable=False),
|
||||
sa.Column("name", sa.String(length=15), nullable=False),
|
||||
sa.Column("mileage", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("sticker_id", sa.Integer(), nullable=False, server_default="1"),
|
||||
sa.Column(
|
||||
"sticker_stock",
|
||||
sa.String(length=256),
|
||||
nullable=False,
|
||||
server_default="0111111111111".ljust(256, "0"),
|
||||
),
|
||||
sa.Column("country", sa.Integer(), nullable=False),
|
||||
sa.Column("store_id", sa.Integer(), nullable=True),
|
||||
sa.Column("password", sa.String(length=10), nullable=False),
|
||||
sa.Column("auto_invite_flag", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("password", name="idac_team_info_password_uk"),
|
||||
sa.UniqueConstraint("id", "version", name="idac_team_info_uk"),
|
||||
)
|
||||
op.create_table(
|
||||
"idac_team_member",
|
||||
sa.Column("id", sa.Integer(), primary_key=True),
|
||||
sa.Column("version", sa.Integer(), nullable=False),
|
||||
sa.Column("team_id", sa.Integer(), nullable=False),
|
||||
sa.Column("user", sa.Integer(), nullable=False),
|
||||
sa.Column("role", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("daily_bonus", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("weekly_bonus", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column(
|
||||
"create_date_daily",
|
||||
sa.TIMESTAMP(),
|
||||
nullable=False,
|
||||
server_default=sa.text("now()"),
|
||||
),
|
||||
sa.Column(
|
||||
"create_date_weekly",
|
||||
sa.TIMESTAMP(),
|
||||
nullable=False,
|
||||
server_default=sa.text("now()"),
|
||||
),
|
||||
sa.Column(
|
||||
"join_dt", sa.TIMESTAMP(), server_default=sa.text("now()"), nullable=False
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["team_id"], ["idac_team_info.id"], onupdate="cascade", ondelete="cascade"
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user"], ["aime_user.id"], onupdate="cascade", ondelete="cascade"
|
||||
),
|
||||
sa.UniqueConstraint("user", "version", name="idac_team_member_uk"),
|
||||
)
|
||||
op.create_table(
|
||||
"idac_league_info",
|
||||
sa.Column("id", sa.Integer(), primary_key=True),
|
||||
sa.Column("version", sa.Integer(), nullable=False),
|
||||
sa.Column("round_id", sa.Integer(), nullable=False),
|
||||
sa.Column("team_id", sa.Integer(), nullable=False),
|
||||
sa.Column("league_rank_id", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("league_point", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.ForeignKeyConstraint(
|
||||
["team_id"], ["idac_team_info.id"], onupdate="cascade", ondelete="cascade"
|
||||
),
|
||||
sa.UniqueConstraint(
|
||||
"version", "round_id", "team_id", name="idac_league_info_uk"
|
||||
),
|
||||
)
|
||||
op.create_table(
|
||||
"idac_user_league_info",
|
||||
sa.Column("id", sa.Integer(), primary_key=True),
|
||||
sa.Column("version", sa.Integer(), nullable=False),
|
||||
sa.Column("user", sa.Integer(), nullable=False),
|
||||
sa.Column("team_id", sa.Integer(), nullable=False),
|
||||
sa.Column("round_id", sa.Integer(), nullable=False),
|
||||
sa.Column("point", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("rewards_claimed", sa.Boolean(), nullable=False, server_default="0"),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user"], ["aime_user.id"], onupdate="cascade", ondelete="cascade"
|
||||
),
|
||||
sa.UniqueConstraint(
|
||||
"user", "version", "round_id", name="idac_user_league_info_uk"
|
||||
),
|
||||
)
|
||||
|
||||
op.drop_column("idac_user_car", "total_car_parts_count")
|
||||
op.drop_column("idac_profile", "team_id")
|
||||
op.drop_column("idac_profile_config", "config_id")
|
||||
|
||||
op.add_column(
|
||||
"idac_profile",
|
||||
sa.Column(
|
||||
"total_car_parts_count", sa.Integer(), nullable=False, server_default="0"
|
||||
),
|
||||
)
|
||||
op.add_column("idac_profile_config", sa.Column("favorite_bgm_list", sa.Text()))
|
||||
op.add_column("idac_profile_config", sa.Column("break_flag", sa.INTEGER()))
|
||||
op.add_column(
|
||||
"idac_profile_config",
|
||||
sa.Column("visible_my_stats", sa.INTEGER()),
|
||||
)
|
||||
op.add_column(
|
||||
"idac_profile_config",
|
||||
sa.Column("visible_rival_stats", sa.INTEGER()),
|
||||
)
|
||||
op.add_column("idac_profile_tips", sa.Column("last_team_join_dt", sa.TIMESTAMP))
|
||||
|
||||
op.create_table(
|
||||
"idac_team_member_info",
|
||||
sa.Column("id", sa.Integer(), primary_key=True),
|
||||
sa.Column("version", sa.Integer(), nullable=False),
|
||||
sa.Column("team_id", sa.Integer(), nullable=False),
|
||||
sa.Column("user", sa.Integer(), nullable=False),
|
||||
sa.Column("action_id", sa.Integer(), nullable=False, server_default="2"),
|
||||
sa.Column(
|
||||
"action_dt", sa.DateTime(), server_default=sa.text("now()"), nullable=False
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["team_id"], ["idac_team_info.id"], onupdate="cascade", ondelete="cascade"
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user"], ["aime_user.id"], onupdate="cascade", ondelete="cascade"
|
||||
),
|
||||
)
|
||||
op.create_index(
|
||||
"idx_trial_ranking",
|
||||
"idac_user_time_trial",
|
||||
["version", "course_id", "style_car_id", "user", "goal_time"],
|
||||
)
|
||||
|
||||
op.alter_column(
|
||||
"idac_user_battle_gift",
|
||||
"gift_status",
|
||||
new_column_name="gift_get_status",
|
||||
existing_type=sa.Integer(),
|
||||
existing_nullable=True,
|
||||
)
|
||||
|
||||
for column_name in PROFILE_STOCK_COLUMNS:
|
||||
op.alter_column(
|
||||
"idac_profile_stock",
|
||||
column_name,
|
||||
existing_type=sa.String(length=255),
|
||||
type_=sa.Text(),
|
||||
existing_nullable=True,
|
||||
)
|
||||
|
||||
op.alter_column(
|
||||
"idac_profile_stock",
|
||||
"mytitle_list",
|
||||
existing_type=sa.String(length=1024),
|
||||
type_=sa.Text(),
|
||||
existing_nullable=True,
|
||||
)
|
||||
op.alter_column(
|
||||
"idac_profile_stock",
|
||||
"mytitle_new_list",
|
||||
existing_type=sa.String(length=1024),
|
||||
type_=sa.Text(),
|
||||
existing_nullable=True,
|
||||
)
|
||||
op.create_unique_constraint(
|
||||
"idac_round_info_uk",
|
||||
"idac_round_info",
|
||||
["round_id_in_json", "season"]
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("idac_team_member_info")
|
||||
op.drop_table("idac_team_member")
|
||||
op.drop_table("idac_league_info")
|
||||
op.drop_table("idac_team_info")
|
||||
op.drop_table("idac_user_league_info")
|
||||
|
||||
op.drop_column("idac_profile", "total_car_parts_count")
|
||||
op.add_column(
|
||||
"idac_user_car",
|
||||
sa.Column(
|
||||
"total_car_parts_count", sa.Integer(), nullable=False, server_default="0"
|
||||
),
|
||||
)
|
||||
|
||||
op.drop_column("idac_profile_config", "favorite_bgm_list")
|
||||
op.drop_column("idac_profile_config", "visible_rival_stats")
|
||||
op.drop_column("idac_profile_config", "visible_my_stats")
|
||||
op.drop_column("idac_profile_config", "break_flag")
|
||||
op.drop_column("idac_profile_tips", "last_team_join_dt")
|
||||
|
||||
op.drop_index("idx_trial_ranking", "idac_user_time_trial")
|
||||
|
||||
op.add_column(
|
||||
"idac_profile",
|
||||
sa.Column("team_id", sa.INTEGER(), server_default="0"),
|
||||
)
|
||||
op.add_column(
|
||||
"idac_profile_config",
|
||||
sa.Column("config_id", sa.INTEGER()),
|
||||
)
|
||||
|
||||
op.alter_column(
|
||||
"idac_user_battle_gift",
|
||||
"gift_get_status",
|
||||
new_column_name="gift_status",
|
||||
existing_type=sa.Integer(),
|
||||
existing_nullable=True,
|
||||
)
|
||||
for column_name in PROFILE_STOCK_COLUMNS:
|
||||
op.alter_column(
|
||||
"idac_profile_stock",
|
||||
column_name,
|
||||
existing_type=sa.Text(),
|
||||
type_=sa.String(length=255),
|
||||
existing_nullable=True,
|
||||
)
|
||||
|
||||
op.alter_column(
|
||||
"idac_profile_stock",
|
||||
"mytitle_list",
|
||||
existing_type=sa.Text(),
|
||||
type_=sa.String(length=1024),
|
||||
existing_nullable=True,
|
||||
)
|
||||
op.alter_column(
|
||||
"idac_profile_stock",
|
||||
"mytitle_new_list",
|
||||
existing_type=sa.Text(),
|
||||
type_=sa.String(length=1024),
|
||||
existing_nullable=True,
|
||||
)
|
||||
op.drop_constraint(
|
||||
"idac_round_info_uk",
|
||||
"idac_round_info",
|
||||
type_="unique"
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
+6
-1
@@ -1,6 +1,8 @@
|
||||
from typing import Dict, List, Tuple, Any
|
||||
import asyncio
|
||||
import json
|
||||
import logging, coloredlogs
|
||||
|
||||
from typing import Dict, List, Tuple, Any
|
||||
from logging.handlers import TimedRotatingFileHandler
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
@@ -140,6 +142,9 @@ class TitleServlet:
|
||||
if hasattr(handler_cls, "setup") and should_call_setup:
|
||||
handler_cls.setup()
|
||||
should_call_setup = False
|
||||
|
||||
if hasattr(handler_cls, "async_init"):
|
||||
asyncio.create_task(handler_cls.async_init())
|
||||
|
||||
self.title_registry[code] = handler_cls
|
||||
|
||||
|
||||
+77
-25
@@ -11,33 +11,85 @@ server:
|
||||
port_matching_p2p: 20003
|
||||
|
||||
timerelease:
|
||||
timerelease_no: 3
|
||||
timerelease_avatar_gacha_no: 3
|
||||
S2:
|
||||
timerelease_no: 4
|
||||
timerelease_avatar_gacha_no: 3
|
||||
S3:
|
||||
timerelease_no: 6
|
||||
timerelease_avatar_gacha_no: 4
|
||||
|
||||
collabo:
|
||||
S2:
|
||||
enable: True
|
||||
collabo_id: 6
|
||||
S3:
|
||||
enable: True
|
||||
collabo_id: 8
|
||||
|
||||
stamp:
|
||||
enable: True
|
||||
enabled_stamps: # max 3 play stamps
|
||||
150:
|
||||
- "touhou_remilia_scarlet"
|
||||
- "touhou_flandre_scarlet"
|
||||
- "touhou_sakuya_izayoi"
|
||||
170:
|
||||
- "touhou_remilia_scarlet"
|
||||
- "touhou_flandre_scarlet"
|
||||
- "touhou_sakuya_izayoi"
|
||||
|
||||
S2:
|
||||
enable: False
|
||||
open_dt: "2024-01-01 00:00:00"
|
||||
close_dt: "2029-12-31 00:00:00"
|
||||
enabled_stamps:
|
||||
150:
|
||||
- "touhou_remilia_scarlet"
|
||||
- "touhou_flandre_scarlet"
|
||||
- "touhou_sakuya_izayoi"
|
||||
S3:
|
||||
enable: False
|
||||
open_dt: "2026-01-01 00:00:00"
|
||||
close_dt: "2029-12-31 00:00:00"
|
||||
enabled_stamps:
|
||||
210:
|
||||
- "touhou_remilia_scarlet"
|
||||
- "touhou_flandre_scarlet"
|
||||
- "touhou_sakuya_izayoi"
|
||||
|
||||
timetrial:
|
||||
enable: True
|
||||
enabled_timetrial:
|
||||
150: "touhou_remilia_scarlet"
|
||||
170: "touhou_remilia_scarlet"
|
||||
S2:
|
||||
enable: TFalsee
|
||||
open_dt: "2024-01-01 00:00:00"
|
||||
close_dt: "2029-12-31 00:00:00"
|
||||
enabled_timetrials:
|
||||
150: "touhou_remilia_scarlet"
|
||||
S3:
|
||||
enable: False
|
||||
open_dt: "2024-01-01 00:00:00"
|
||||
close_dt: "2029-12-31 00:00:00"
|
||||
enabled_timetrials:
|
||||
210: "touhou_remilia_scarlet"
|
||||
|
||||
battle_event:
|
||||
enable: True
|
||||
enabled_battle_event:
|
||||
170: "touhou_1st"
|
||||
battle_gift:
|
||||
S2:
|
||||
enable: False
|
||||
open_dt: "2024-01-01 00:00:00"
|
||||
close_dt: "2029-12-31 00:00:00"
|
||||
enabled_battle_gifts:
|
||||
150: "touhou_1st"
|
||||
S3:
|
||||
enable: False
|
||||
open_dt: "2024-01-01 00:00:00"
|
||||
close_dt: "2029-12-31 00:00:00"
|
||||
enabled_battle_gifts:
|
||||
210: "touhou_1st"
|
||||
|
||||
round_event:
|
||||
enable: True
|
||||
enabled_round: "S2R2"
|
||||
last_round: "S2R1"
|
||||
round:
|
||||
S2:
|
||||
enable: True
|
||||
open_dt: "2024-01-01 00:00:00"
|
||||
close_dt: "2029-02-28 00:00:00"
|
||||
enabled_round: "round_S2R2"
|
||||
last_round: "round_S2R1"
|
||||
S3:
|
||||
enable: True
|
||||
open_dt: "2024-01-01 00:00:00"
|
||||
close_dt: "2029-02-28 00:00:00"
|
||||
open_round: True
|
||||
enabled_round: "round_S3R1"
|
||||
last_round: ""
|
||||
|
||||
league_reward:
|
||||
S3:
|
||||
enable: True
|
||||
enabled_league_reward: "league_reward_S3"
|
||||
|
||||
@@ -22,3 +22,4 @@ asyncio
|
||||
uvicorn
|
||||
alembic
|
||||
python-multipart
|
||||
pydantic
|
||||
|
||||
+12
-1
@@ -9,8 +9,19 @@ from titles.idac.database import IDACData
|
||||
class IDACBase:
|
||||
def __init__(self, core_cfg: CoreConfig, game_cfg: IDACConfig) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.game_config = game_cfg
|
||||
|
||||
self.server_cfg = game_cfg.server
|
||||
self.game_cfg = game_cfg.season1
|
||||
|
||||
self.game = IDACConstants.GAME_CODE
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_1
|
||||
self.data = IDACData(core_cfg)
|
||||
self.logger = logging.getLogger("idac")
|
||||
|
||||
self.controllers = dict()
|
||||
|
||||
async def async_init(self):
|
||||
"""
|
||||
Allows initialization of async functions such as database transactions
|
||||
"""
|
||||
pass
|
||||
|
||||
+16
-143
@@ -1,5 +1,8 @@
|
||||
from typing import Dict
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.gameconfig.season1_config import IDACSeason1Config
|
||||
from titles.idac.gameconfig.season2_config import IDACSeason2Config
|
||||
from titles.idac.gameconfig.season3_config import IDACSeason3Config
|
||||
from titles.idac.gameconfig.season4_config import IDACSeason4Config
|
||||
|
||||
|
||||
class IDACServerConfig:
|
||||
@@ -67,150 +70,20 @@ class IDACServerConfig:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "idac", "server", "port_matching_p2p", default=20003
|
||||
)
|
||||
|
||||
class IDACTimereleaseConfig:
|
||||
def __init__(self, parent: "IDACConfig") -> None:
|
||||
self.__config = parent
|
||||
|
||||
@property
|
||||
def timerelease_no(self) -> int:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "idac", "timerelease", "timerelease_no", default=1
|
||||
)
|
||||
|
||||
@property
|
||||
def timerelease_avatar_gacha_no(self) -> int:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "idac", "timerelease", "timerelease_avatar_gacha_no", default=1
|
||||
)
|
||||
|
||||
|
||||
class IDACStampConfig:
|
||||
def __init__(self, parent: "IDACConfig") -> None:
|
||||
self.__config = parent
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "idac", "stamp", "enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def enabled_stamps(self) -> Dict:
|
||||
"""
|
||||
In the form of:
|
||||
<version as int>:
|
||||
- <stamp name 1>
|
||||
- <stamp name 2>
|
||||
- <stamp name 3>
|
||||
|
||||
max 3 stamps per version
|
||||
|
||||
f.e.:
|
||||
150:
|
||||
- "touhou_remilia_scarlet"
|
||||
- "touhou_flandre_scarlet"
|
||||
- "touhou_sakuya_izayoi"
|
||||
"""
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config,
|
||||
"idac",
|
||||
"stamp",
|
||||
"enabled_stamps",
|
||||
default={},
|
||||
)
|
||||
|
||||
|
||||
class IDACTimetrialConfig:
|
||||
def __init__(self, parent: "IDACConfig") -> None:
|
||||
self.__config = parent
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "idac", "timetrial", "enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def enabled_timetrial(self) -> Dict:
|
||||
"""
|
||||
In the form of:
|
||||
<version as int>: <timetrial name>
|
||||
|
||||
f.e.:
|
||||
150: "touhou_remilia_scarlet"
|
||||
"""
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config,
|
||||
"idac",
|
||||
"timetrial",
|
||||
"enabled_timetrial",
|
||||
default={},
|
||||
)
|
||||
|
||||
class IDACTBattleGiftConfig:
|
||||
def __init__(self, parent: "IDACConfig") -> None:
|
||||
self.__config = parent
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "idac", "battle_event", "enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def enabled_battle_event(self) -> Dict:
|
||||
"""
|
||||
In the form of:
|
||||
<version as int>: <battle_event name>
|
||||
|
||||
f.e.:
|
||||
170: "touhou_1st"
|
||||
"""
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config,
|
||||
"idac",
|
||||
"battle_event",
|
||||
"enabled_battle_event",
|
||||
default={},
|
||||
)
|
||||
|
||||
class IDACRoundConfig:
|
||||
def __init__(self, parent: "IDACConfig") -> None:
|
||||
self.__config = parent
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "idac", "round_event", "enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def enabled_round(self) -> str:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config,
|
||||
"idac",
|
||||
"round_event",
|
||||
"enabled_round",
|
||||
default="S2R2",
|
||||
)
|
||||
|
||||
@property
|
||||
def last_round(self) -> str:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config,
|
||||
"idac",
|
||||
"round_event",
|
||||
"last_round",
|
||||
default="S2R1",
|
||||
)
|
||||
|
||||
|
||||
class IDACConfig(dict):
|
||||
def __init__(self) -> None:
|
||||
self.server = IDACServerConfig(self)
|
||||
self.timerelease = IDACTimereleaseConfig(self)
|
||||
self.stamp = IDACStampConfig(self)
|
||||
self.timetrial = IDACTimetrialConfig(self)
|
||||
self.battle_event = IDACTBattleGiftConfig(self)
|
||||
self.round_event = IDACRoundConfig(self)
|
||||
self.season1 = IDACSeason1Config(self)
|
||||
self.season2 = IDACSeason2Config(self)
|
||||
self.season3 = IDACSeason3Config(self)
|
||||
self.season4 = IDACSeason4Config(self)
|
||||
|
||||
def update(self, *args, **kwargs):
|
||||
super().update(*args, **kwargs)
|
||||
|
||||
self.season1.load_events()
|
||||
self.season2.load_events()
|
||||
self.season3.load_events()
|
||||
self.season4.load_events()
|
||||
|
||||
+302
-1
@@ -1,10 +1,192 @@
|
||||
class IDACConstants():
|
||||
from enum import IntEnum
|
||||
from typing import Dict, List, Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class RewardCategoryEnum(IntEnum):
|
||||
NONE = 0
|
||||
D_COIN = 1
|
||||
CAR_DRESSUP_TOKEN = 3
|
||||
AVATAR_TOKEN = 5
|
||||
TACHOMETER = 6
|
||||
AURA = 7
|
||||
AURA_COLOR = 8
|
||||
AVATAR_FACE = 9
|
||||
AVATAR_EYE = 10
|
||||
AVATAR_MOUTH = 11
|
||||
AVATAR_HAIR = 12
|
||||
AVATAR_GLASSES = 13
|
||||
AVATAR_ACCESSORIES = 14
|
||||
AVATAR_BODY = 15
|
||||
AVATAR_BACKGROUNDS = 18
|
||||
CHATSTAMP = 21
|
||||
KEYCHAIN = 22
|
||||
TITLE = 24
|
||||
FULLTUNE_TICKET = 25
|
||||
PAPER_CUP = 26
|
||||
BGM = 27
|
||||
ONOMATOPE = 28
|
||||
MAIN_MENU_BG = 31
|
||||
CAR_PAINT = 32
|
||||
AURA_LEVEL = 33
|
||||
FT_TOKEN_PIECES = 34
|
||||
UNDERNEONS = 35
|
||||
|
||||
|
||||
class LayoutEnum(IntEnum):
|
||||
TECHNICAL = 0
|
||||
HIGHSPEED = 1
|
||||
|
||||
|
||||
class StyleEnum(IntEnum):
|
||||
DH = 0
|
||||
HC = 1
|
||||
AR = 2
|
||||
|
||||
|
||||
class CarMakerEnum(IntEnum):
|
||||
TOYOTA = 0
|
||||
NISSAN = 1
|
||||
HONDA = 2
|
||||
MAZDA = 3
|
||||
SUBARU = 4
|
||||
MITSUBISHI = 5
|
||||
SUZUKI = 6
|
||||
INITIALD = 7
|
||||
COMPLETE = 8
|
||||
PORSCHE = 9
|
||||
|
||||
|
||||
class StoryTypeEnum(IntEnum):
|
||||
NONE = 0
|
||||
STORY = 1
|
||||
MFG = 2
|
||||
BUNTA = 3
|
||||
TOUHOU_1ST = 4
|
||||
MIKU = 5
|
||||
HOT_VERSION = 6
|
||||
|
||||
|
||||
class CollaboEnum(IntEnum):
|
||||
NONE = 0
|
||||
WMN = 1
|
||||
TOHO = 2
|
||||
LIFEGUARD = 3
|
||||
MFG = 4
|
||||
ANNIVERSARY_20TH = 5
|
||||
TOHO_2 = 6
|
||||
MIKU = 7
|
||||
HOT_VERSION = 8
|
||||
TOUHOU_3 = 9
|
||||
|
||||
|
||||
class StoryPlayStatusEnum(IntEnum):
|
||||
NEW = 0
|
||||
PLAYED = 1
|
||||
CLEARED = 2
|
||||
MAX = 3 # not used?
|
||||
|
||||
|
||||
class NameChangeCategoryEnum(IntEnum):
|
||||
PAYMET = 0
|
||||
CREDIT_RETRY = 1
|
||||
INHERIT = 2
|
||||
DENIED = 3
|
||||
|
||||
|
||||
class BattleGiftStatusEnum(IntEnum):
|
||||
NONE = 0
|
||||
AQUIRED = 1
|
||||
NEW = 2
|
||||
|
||||
|
||||
class LeagueRankEnum(IntEnum):
|
||||
OPEN = 0
|
||||
BASIC = 1
|
||||
BRONZE = 2
|
||||
SILVER = 3
|
||||
GOLD = 4
|
||||
PLATINUM = 5
|
||||
MASTER = 6
|
||||
|
||||
|
||||
class VSInfoBattleModeEnum(IntEnum):
|
||||
ONLINE = 0
|
||||
INSTORE = 1
|
||||
|
||||
|
||||
class PointRule(BaseModel):
|
||||
upper_points: int
|
||||
lower_points: int
|
||||
destination_league_rank_id: LeagueRankEnum
|
||||
destination_point: int
|
||||
|
||||
|
||||
class RankRule(BaseModel):
|
||||
destination_league_rank_id: LeagueRankEnum
|
||||
destination_rank: int
|
||||
demote_rank: Optional[int] = None
|
||||
|
||||
|
||||
class PointRankingRule(BaseModel):
|
||||
points_upper_rank: int
|
||||
upper_points: int
|
||||
point_rules: List[PointRule]
|
||||
|
||||
|
||||
class OpenRankingRule(BaseModel):
|
||||
default_rank_id: LeagueRankEnum
|
||||
default_rank: int
|
||||
rules: List[RankRule]
|
||||
|
||||
|
||||
class NormalRankingRule(BaseModel):
|
||||
default_rank_id: LeagueRankEnum
|
||||
default_rank: int
|
||||
rules: Dict[LeagueRankEnum, RankRule]
|
||||
|
||||
|
||||
class TeamPointsRankingRules(BaseModel):
|
||||
"""
|
||||
TeamPointsRankingRules is a dictionary of PointRankingRule objects, where the key is the version (season)
|
||||
"""
|
||||
|
||||
RootModel: Dict[int, PointRankingRule]
|
||||
|
||||
def __getitem__(self, version: int) -> "PointRankingRule":
|
||||
return self.RootModel[version]
|
||||
|
||||
|
||||
class TeamRankingRulesOpenRound(BaseModel):
|
||||
"""
|
||||
TeamRankingRulesOpenRound is a dictionary of RankingRule objects, where the key is the version (season)
|
||||
"""
|
||||
|
||||
RootModel: Dict[int, OpenRankingRule]
|
||||
|
||||
def __getitem__(self, version: int) -> "OpenRankingRule":
|
||||
return self.RootModel[version]
|
||||
|
||||
|
||||
class TeamRankingRulesNormalRound(BaseModel):
|
||||
"""
|
||||
TeamRankingRulesNormalRound is a dictionary of RankingRule objects, where the key is the version (season)
|
||||
"""
|
||||
|
||||
RootModel: Dict[int, NormalRankingRule]
|
||||
|
||||
def __getitem__(self, version: int) -> "NormalRankingRule":
|
||||
return self.RootModel[version]
|
||||
|
||||
|
||||
class IDACConstants:
|
||||
GAME_CODE = "SDGT"
|
||||
|
||||
CONFIG_NAME = "idac.yaml"
|
||||
|
||||
VER_IDAC_SEASON_1 = 0
|
||||
VER_IDAC_SEASON_2 = 1
|
||||
VER_IDAC_SEASON_3 = 2
|
||||
|
||||
BATTLE_MODE_ONLINE = 0
|
||||
BATTLE_MODE_OFFLINE = 1
|
||||
@@ -12,6 +194,125 @@ class IDACConstants():
|
||||
VERSION_STRING = (
|
||||
"Initial D THE ARCADE Season 1",
|
||||
"Initial D THE ARCADE Season 2",
|
||||
"Initial D THE ARCADE Season 3",
|
||||
)
|
||||
|
||||
MAX_GRADE = {
|
||||
0: 54,
|
||||
1: 72,
|
||||
2: 72,
|
||||
3: 81,
|
||||
}
|
||||
|
||||
CAR_LAYOUT_MAPPING_FULL = {
|
||||
0: LayoutEnum.TECHNICAL,
|
||||
1: LayoutEnum.TECHNICAL,
|
||||
2: LayoutEnum.TECHNICAL,
|
||||
3: LayoutEnum.TECHNICAL,
|
||||
4: LayoutEnum.TECHNICAL,
|
||||
5: LayoutEnum.TECHNICAL,
|
||||
9: LayoutEnum.TECHNICAL,
|
||||
10: LayoutEnum.TECHNICAL,
|
||||
13: LayoutEnum.TECHNICAL,
|
||||
14: LayoutEnum.TECHNICAL,
|
||||
258: LayoutEnum.TECHNICAL,
|
||||
259: LayoutEnum.TECHNICAL,
|
||||
261: LayoutEnum.TECHNICAL,
|
||||
512: LayoutEnum.TECHNICAL,
|
||||
513: LayoutEnum.TECHNICAL,
|
||||
514: LayoutEnum.TECHNICAL,
|
||||
515: LayoutEnum.TECHNICAL,
|
||||
770: LayoutEnum.TECHNICAL,
|
||||
771: LayoutEnum.TECHNICAL,
|
||||
772: LayoutEnum.TECHNICAL,
|
||||
1280: LayoutEnum.TECHNICAL,
|
||||
1281: LayoutEnum.TECHNICAL,
|
||||
1283: LayoutEnum.TECHNICAL,
|
||||
1285: LayoutEnum.TECHNICAL,
|
||||
1286: LayoutEnum.TECHNICAL,
|
||||
1536: LayoutEnum.TECHNICAL,
|
||||
1537: LayoutEnum.TECHNICAL,
|
||||
1792: LayoutEnum.TECHNICAL,
|
||||
}
|
||||
|
||||
TEAM_POINTS_RANKING_RULES = TeamPointsRankingRules(
|
||||
RootModel={
|
||||
VER_IDAC_SEASON_3: PointRankingRule(
|
||||
points_upper_rank=2,
|
||||
upper_points=5000,
|
||||
point_rules=[
|
||||
PointRule(
|
||||
lower_points=0,
|
||||
upper_points=1000,
|
||||
destination_league_rank_id=LeagueRankEnum.BASIC,
|
||||
destination_point=1000,
|
||||
),
|
||||
PointRule(
|
||||
lower_points=1000,
|
||||
upper_points=5000,
|
||||
destination_league_rank_id=LeagueRankEnum.BRONZE,
|
||||
destination_point=5000,
|
||||
),
|
||||
],
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
TEAM_RANKING_RULES_OPEN_ROUND = TeamRankingRulesOpenRound(
|
||||
RootModel={
|
||||
VER_IDAC_SEASON_3: OpenRankingRule(
|
||||
default_rank_id=LeagueRankEnum.SILVER,
|
||||
default_rank=2200,
|
||||
rules=[
|
||||
RankRule(
|
||||
destination_league_rank_id=LeagueRankEnum.GOLD,
|
||||
destination_rank=2200,
|
||||
demote_rank=None,
|
||||
),
|
||||
RankRule(
|
||||
destination_league_rank_id=LeagueRankEnum.PLATINUM,
|
||||
destination_rank=700,
|
||||
demote_rank=None,
|
||||
),
|
||||
RankRule(
|
||||
destination_league_rank_id=LeagueRankEnum.MASTER,
|
||||
destination_rank=200,
|
||||
demote_rank=None,
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
TEAM_RANKING_RULES_NORMAL_ROUND = TeamRankingRulesNormalRound(
|
||||
RootModel={
|
||||
VER_IDAC_SEASON_3: NormalRankingRule(
|
||||
default_rank_id=LeagueRankEnum.SILVER,
|
||||
default_rank=500,
|
||||
rules={
|
||||
LeagueRankEnum.SILVER: RankRule(
|
||||
destination_league_rank_id=LeagueRankEnum.GOLD,
|
||||
destination_rank=500,
|
||||
demote_rank=None,
|
||||
),
|
||||
LeagueRankEnum.GOLD: RankRule(
|
||||
destination_league_rank_id=LeagueRankEnum.PLATINUM,
|
||||
destination_rank=200,
|
||||
demote_rank=1001,
|
||||
),
|
||||
LeagueRankEnum.PLATINUM: RankRule(
|
||||
destination_league_rank_id=LeagueRankEnum.MASTER,
|
||||
destination_rank=50,
|
||||
demote_rank=301,
|
||||
),
|
||||
LeagueRankEnum.MASTER: RankRule(
|
||||
destination_league_rank_id=LeagueRankEnum.MASTER,
|
||||
destination_rank=50,
|
||||
demote_rank=151,
|
||||
),
|
||||
},
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import logging
|
||||
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.gameconfig.season2_config import IDACSeason2Config
|
||||
from titles.idac.model.season2.advertise_model import (
|
||||
RequestSeason2AdvertiseGetRankingData,
|
||||
ResponseSeason2AdvertiseGetRankingData,
|
||||
)
|
||||
from titles.idac.model.season2.common_model import RequestSeason2Headers
|
||||
from titles.idac.service.season2.advertise_service import IDACSeason2AdvertiseService
|
||||
|
||||
|
||||
class IDACSeason2AdvertiseController:
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason2Config,
|
||||
advertise_service: IDACSeason2AdvertiseService,
|
||||
) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.server_cfg = server_cfg
|
||||
self.game_config = game_cfg
|
||||
|
||||
self.game = IDACConstants.GAME_CODE
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||
self.logger = logging.getLogger("idac")
|
||||
|
||||
self.advertise_service = advertise_service
|
||||
|
||||
async def handle_get_ranking_data_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason2AdvertiseGetRankingData:
|
||||
req = RequestSeason2AdvertiseGetRankingData.model_validate(data)
|
||||
req_headers = RequestSeason2Headers.model_validate(headers)
|
||||
|
||||
national_best_data = await self.advertise_service.get_national_best_data(
|
||||
req.last_update_date
|
||||
)
|
||||
|
||||
return ResponseSeason2AdvertiseGetRankingData(
|
||||
national_best_data=national_best_data
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
from datetime import datetime
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.gameconfig.season2_config import IDACSeason2Config
|
||||
from titles.idac.model.season2.alive_model import ResponseSeason2AliveGet
|
||||
|
||||
|
||||
class IDACSeason2AliveController:
|
||||
ResponseAliveGet = ResponseSeason2AliveGet
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason2Config,
|
||||
) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.server_cfg = server_cfg
|
||||
self.game_cfg = game_cfg
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||
|
||||
async def handle_get_request(self, data: Dict, headers: Dict) -> ResponseAliveGet:
|
||||
return self.ResponseAliveGet(
|
||||
server_status=1,
|
||||
force_reboot_time=int(datetime.now().timestamp()) - 86400,
|
||||
)
|
||||
@@ -0,0 +1,311 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
from core.utils import Utils
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import (
|
||||
CollaboEnum,
|
||||
IDACConstants,
|
||||
RewardCategoryEnum,
|
||||
StoryTypeEnum,
|
||||
)
|
||||
from titles.idac.gameconfig.season2_config import IDACSeason2Config
|
||||
from titles.idac.model.common_model import ResponseGeneric, Reward
|
||||
from titles.idac.model.season2.boot_model import (
|
||||
ResponseSeason2BootGetConfigData,
|
||||
ResponseSeason2BootGetGachaData,
|
||||
ResponseSeason2BootGetTimeReleaseData,
|
||||
)
|
||||
|
||||
|
||||
class IDACSeason2BootController:
|
||||
ResponseBootGetConfigData = ResponseSeason2BootGetConfigData
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason2Config,
|
||||
) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.server_cfg = server_cfg
|
||||
self.game_cfg = game_cfg
|
||||
|
||||
self.game = IDACConstants.GAME_CODE
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||
self.logger = logging.getLogger("idac")
|
||||
|
||||
# load timerelease numbers from config
|
||||
self.timerelease_no = self.game_cfg.timerelease.timerelease_no
|
||||
self.timerelease_avatar_gacha_no = (
|
||||
self.game_cfg.timerelease.timerelease_avatar_gacha_no
|
||||
)
|
||||
|
||||
self.collabo_id = CollaboEnum.NONE
|
||||
|
||||
self.stamp_info: dict[int, list[ResponseSeason2BootGetConfigData.StampInfo]] = (
|
||||
defaultdict(list)
|
||||
)
|
||||
self.timetrial_event: dict[
|
||||
int, ResponseSeason2BootGetConfigData.TimetrialEventData
|
||||
] = defaultdict()
|
||||
|
||||
self.battle_gift_event: dict[
|
||||
int, ResponseSeason2BootGetConfigData.BattleGiftEventMaster
|
||||
] = defaultdict()
|
||||
|
||||
self.round_event: list[ResponseSeason2BootGetConfigData.RoundEvent_2] = []
|
||||
self.last_round_event: list[ResponseSeason2BootGetConfigData.RoundEvent_2] = []
|
||||
self.last_round_event_ranking: list[
|
||||
ResponseSeason2BootGetConfigData.LastRoundEventRanking
|
||||
] = []
|
||||
|
||||
if self.game_cfg.collabo.enable:
|
||||
self.collabo_id = CollaboEnum(self.game_cfg.collabo.collabo_id)
|
||||
|
||||
if self.game_cfg.stamp.enable:
|
||||
self.stamp_info = self.game_cfg.stamp_info
|
||||
|
||||
if self.game_cfg.timetrial.enable:
|
||||
self.timetrial_event = self.game_cfg.timetrial_info
|
||||
|
||||
if self.game_cfg.battle_gift.enable:
|
||||
self.battle_gift_event = self.game_cfg.battle_gift_info
|
||||
|
||||
if self.game_cfg.round.enable:
|
||||
self.round_event = [self.game_cfg.round_info]
|
||||
|
||||
if self.game_cfg.last_round_info:
|
||||
self.last_round_event = [self.game_cfg.last_round_info]
|
||||
|
||||
def _headers_to_version(self, headers: Dict) -> int:
|
||||
version = headers.get("device_version", "1.00.00")
|
||||
return int(version.replace(".", "")[:-2])
|
||||
|
||||
def _special_story_type(self, headers: Dict) -> StoryTypeEnum:
|
||||
ver = self._headers_to_version(headers)
|
||||
|
||||
return {
|
||||
150: StoryTypeEnum.TOUHOU_1ST,
|
||||
170: StoryTypeEnum.MIKU,
|
||||
210: StoryTypeEnum.HOT_VERSION,
|
||||
}.get(ver, 0)
|
||||
|
||||
async def handle_get_config_data_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason2BootGetConfigData:
|
||||
ver_str = self._headers_to_version(headers)
|
||||
|
||||
# TODO: properly set the domain name
|
||||
hostname = "sega-initiald.net"
|
||||
|
||||
if self.core_cfg.server.is_using_proxy:
|
||||
domain_api_game = f"http://{hostname}/{ver_str}/"
|
||||
else:
|
||||
domain_api_game = (
|
||||
f"http://{hostname}:{Utils.get_title_port(self.core_cfg)}/{ver_str}/"
|
||||
)
|
||||
|
||||
# sets the special mode data if the version supports the special mode
|
||||
special_mode_data = None
|
||||
special_story_type = self._special_story_type(headers)
|
||||
if special_story_type != StoryTypeEnum.NONE:
|
||||
special_mode_data = ResponseSeason2BootGetConfigData.SpecialModeData(
|
||||
start_dt=datetime(2023, 1, 1),
|
||||
end_dt=datetime(2029, 12, 31),
|
||||
story_type=special_story_type,
|
||||
)
|
||||
|
||||
battle_gift_event = []
|
||||
if ver_str in self.battle_gift_event:
|
||||
battle_gift_event_data = self.battle_gift_event.get(ver_str, {})
|
||||
# check if battle gift event time is valid
|
||||
start_dt = battle_gift_event_data.start_dt
|
||||
end_dt = battle_gift_event_data.end_dt
|
||||
|
||||
if start_dt < datetime.now() < end_dt:
|
||||
battle_gift_event = [battle_gift_event_data]
|
||||
|
||||
stamp_info: list[ResponseSeason2BootGetConfigData.StampInfo] = []
|
||||
if ver_str in self.stamp_info:
|
||||
stamp_info_data = self.stamp_info.get(ver_str, [])
|
||||
# check if stamp event time is valid
|
||||
start_dt = self.game_cfg.stamp.open_dt
|
||||
end_dt = self.game_cfg.stamp.close_dt
|
||||
|
||||
if start_dt < datetime.now() < end_dt:
|
||||
stamp_info = stamp_info_data
|
||||
|
||||
number_lottery_data: list[
|
||||
ResponseSeason2BootGetConfigData.NumberLotteryData
|
||||
] = [
|
||||
self.ResponseBootGetConfigData.NumberLotteryData(
|
||||
m_number_lottery_win_number_no=10,
|
||||
m_number_lottery_schedule_no=1,
|
||||
win_number=0,
|
||||
reward_category=RewardCategoryEnum.FT_TOKEN_PIECES,
|
||||
reward_type=1,
|
||||
end_dt=0,
|
||||
),
|
||||
self.ResponseBootGetConfigData.NumberLotteryData(
|
||||
m_number_lottery_win_number_no=11,
|
||||
m_number_lottery_schedule_no=1,
|
||||
win_number=1111,
|
||||
reward_category=RewardCategoryEnum.FT_TOKEN_PIECES,
|
||||
reward_type=1,
|
||||
end_dt=0,
|
||||
),
|
||||
self.ResponseBootGetConfigData.NumberLotteryData(
|
||||
m_number_lottery_win_number_no=12,
|
||||
m_number_lottery_schedule_no=1,
|
||||
win_number=2222,
|
||||
reward_category=RewardCategoryEnum.FT_TOKEN_PIECES,
|
||||
reward_type=1,
|
||||
end_dt=0,
|
||||
),
|
||||
self.ResponseBootGetConfigData.NumberLotteryData(
|
||||
m_number_lottery_win_number_no=13,
|
||||
m_number_lottery_schedule_no=1,
|
||||
win_number=3333,
|
||||
reward_category=RewardCategoryEnum.FT_TOKEN_PIECES,
|
||||
reward_type=1,
|
||||
end_dt=0,
|
||||
),
|
||||
self.ResponseBootGetConfigData.NumberLotteryData(
|
||||
m_number_lottery_win_number_no=14,
|
||||
m_number_lottery_schedule_no=1,
|
||||
win_number=4444,
|
||||
reward_category=RewardCategoryEnum.FT_TOKEN_PIECES,
|
||||
reward_type=1,
|
||||
end_dt=0,
|
||||
),
|
||||
self.ResponseBootGetConfigData.NumberLotteryData(
|
||||
m_number_lottery_win_number_no=15,
|
||||
m_number_lottery_schedule_no=1,
|
||||
win_number=5555,
|
||||
reward_category=RewardCategoryEnum.FT_TOKEN_PIECES,
|
||||
reward_type=1,
|
||||
end_dt=0,
|
||||
),
|
||||
self.ResponseBootGetConfigData.NumberLotteryData(
|
||||
m_number_lottery_win_number_no=16,
|
||||
m_number_lottery_schedule_no=1,
|
||||
win_number=6666,
|
||||
reward_category=RewardCategoryEnum.FT_TOKEN_PIECES,
|
||||
reward_type=1,
|
||||
end_dt=0,
|
||||
),
|
||||
self.ResponseBootGetConfigData.NumberLotteryData(
|
||||
m_number_lottery_win_number_no=17,
|
||||
m_number_lottery_schedule_no=1,
|
||||
win_number=7777,
|
||||
reward_category=RewardCategoryEnum.FT_TOKEN_PIECES,
|
||||
reward_type=1,
|
||||
end_dt=0,
|
||||
),
|
||||
self.ResponseBootGetConfigData.NumberLotteryData(
|
||||
m_number_lottery_win_number_no=18,
|
||||
m_number_lottery_schedule_no=1,
|
||||
win_number=8888,
|
||||
reward_category=RewardCategoryEnum.FT_TOKEN_PIECES,
|
||||
reward_type=1,
|
||||
end_dt=0,
|
||||
),
|
||||
self.ResponseBootGetConfigData.NumberLotteryData(
|
||||
m_number_lottery_win_number_no=19,
|
||||
m_number_lottery_schedule_no=1,
|
||||
win_number=9999,
|
||||
reward_category=RewardCategoryEnum.FT_TOKEN_PIECES,
|
||||
reward_type=1,
|
||||
end_dt=0,
|
||||
),
|
||||
]
|
||||
|
||||
return self.ResponseBootGetConfigData(
|
||||
domain_api_game=domain_api_game,
|
||||
domain_matching=f"{domain_api_game}initiald-matching/",
|
||||
domain_echo1=f"{hostname}:{self.server_cfg.echo1}",
|
||||
domain_echo2=f"{hostname}:{self.server_cfg.echo2}",
|
||||
domain_ping=f"{hostname}",
|
||||
battle_gift_event_master=battle_gift_event,
|
||||
# online battle round event
|
||||
round_event=self.round_event,
|
||||
last_round_event=self.last_round_event,
|
||||
last_round_event_ranking=self.last_round_event_ranking,
|
||||
stamp_info=stamp_info,
|
||||
# 0 = use default data, 1+ = server version of timereleasedata response
|
||||
timerelease_no=self.timerelease_no,
|
||||
# 0 = use default data, 1+ = server version of gachadata response
|
||||
timerelease_avatar_gacha_no=self.timerelease_avatar_gacha_no,
|
||||
timetrial_disp_date=int(
|
||||
datetime.strptime("2023-10-01", "%Y-%m-%d").timestamp()
|
||||
),
|
||||
collabo_id=self.collabo_id,
|
||||
theory_open_version="1.40.00",
|
||||
# unlocks the version specific special mode
|
||||
special_mode_data=special_mode_data,
|
||||
timetrial_event_data=self.timetrial_event.get(ver_str, None),
|
||||
number_lottery_data=number_lottery_data,
|
||||
)
|
||||
|
||||
async def handle_bookkeep_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseGeneric:
|
||||
return ResponseGeneric()
|
||||
|
||||
async def handle_get_gacha_data_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason2BootGetGachaData:
|
||||
# TODO: add support for multiple seasons (maybe even multiple vesions of the same season?)
|
||||
with open("./titles/idac/data/avatarGacha.json", encoding="UTF-8") as f:
|
||||
avatar_gacha_data = f.read()
|
||||
|
||||
resp = ResponseSeason2BootGetGachaData.model_validate_json(avatar_gacha_data)
|
||||
return resp
|
||||
|
||||
async def handle_get_timerelease_data_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason2BootGetTimeReleaseData:
|
||||
"""
|
||||
timerelease chapter:
|
||||
1 = Story: 1, 2, 3, 4, 5, 6, 7, 8, 9, 19 (Chapter 10), 29 (Chapter 11)
|
||||
2 = MF Ghost: 10, 11, 12, 13, 14
|
||||
3 = Bunta: 15, 16, 17, 18, 20, 21, 21, 22
|
||||
4 = Touhou Project Special Event: 23, 24, 25, 26, 27, 28
|
||||
5 = Hatsune Miku Special Event: 36, 37, 38
|
||||
"""
|
||||
path = "./titles/idac/data/"
|
||||
|
||||
# 1.00.00 is default
|
||||
device_version_data = headers.get("device_version", "1.00.00")
|
||||
device_version = int(device_version_data.replace(".", "")[:-2])
|
||||
|
||||
timerelease_filename = f"timeRelease_v{device_version:04d}"
|
||||
timerelease_path = f"{path}{timerelease_filename}.json"
|
||||
|
||||
# if the file doesn't exist, try to find the next lowest version
|
||||
if not os.path.exists(timerelease_path):
|
||||
while device_version > 100:
|
||||
device_version -= 1
|
||||
timerelease_filename = f"timeRelease_v{device_version:04d}"
|
||||
timerelease_path = f"{path}{timerelease_filename}.json"
|
||||
|
||||
# if the file exists, break out of the loop
|
||||
if os.path.exists(timerelease_path):
|
||||
break
|
||||
|
||||
self.logger.debug(f"Using time release file: {timerelease_filename}")
|
||||
# load the time release data
|
||||
with open(f"{path}{timerelease_filename}.json", encoding="utf-8") as f:
|
||||
time_release_data = f.read()
|
||||
|
||||
resp = ResponseSeason2BootGetTimeReleaseData.model_validate_json(
|
||||
time_release_data
|
||||
)
|
||||
|
||||
return resp
|
||||
@@ -0,0 +1,500 @@
|
||||
import logging
|
||||
|
||||
from typing import Dict
|
||||
from datetime import timedelta
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants, NameChangeCategoryEnum
|
||||
from titles.idac.gameconfig.season2_config import IDACSeason2Config
|
||||
from titles.idac.model.season2.common_model import RequestSeason2UserHeaders
|
||||
from titles.idac.model.season2.factory_model import *
|
||||
from titles.idac.model.season2.item_model import TicketDto
|
||||
from titles.idac.model.season2.user_model import ProfileDto
|
||||
from titles.idac.service.season2.factory_service import IDACSeason2FactoryService
|
||||
from titles.idac.service.season2.user_service import IDACSeason2UserService
|
||||
|
||||
|
||||
class IDACSeason2FactoryController:
|
||||
RequestFactoryGetCarData = RequestSeason2FactoryGetCarData
|
||||
ResponseFactoryGetCarData = ResponseSeason2FactoryGetCarData
|
||||
|
||||
RequestFactoryAvatarGachaResult = RequestSeason2FactoryAvatarGachaResult
|
||||
ResponseFactoryAvatarGachaResult = ResponseSeason2FactoryAvatarGachaResult
|
||||
|
||||
RequestFactoryBuyCarResult = RequestSeason2FactoryBuyCarResult
|
||||
ResponseFactoryBuyCarResult = ResponseSeason2FactoryBuyCarResult
|
||||
|
||||
RequestFactorySaveFavoriteCar = RequestSeason2FactorySaveFavoriteCar
|
||||
ResponseFactorySaveFavoriteCar = ResponseSeason2FactorySaveFavoriteCar
|
||||
|
||||
RequestFactoryUpdateCustomizeResult = RequestSeason2FactoryUpdateCustomizeResult
|
||||
ResponseFactoryUpdateCustomizeResult = ResponseSeason2FactoryUpdateCustomizeResult
|
||||
|
||||
RequestFactoryUpdateMultipleCustomizeResult = (
|
||||
RequestSeason2FactoryUpdateMultipleCustomizeResult
|
||||
)
|
||||
ResponseFactoryUpdateMultipleCustomizeResult = (
|
||||
ResponseSeason2FactoryUpdateMultipleCustomizeResult
|
||||
)
|
||||
|
||||
RequestFactoryUpdateCustomizeUser = RequestSeason2FactoryUpdateCustomizeUser
|
||||
ResponseFactoryUpdateCustomizeUser = ResponseSeason2FactoryUpdateCustomizeUser
|
||||
|
||||
RequestFactoryUpdateCustomizeAvatar = RequestSeason2FactoryUpdateCustomizeAvatar
|
||||
ResponseFactoryUpdateCustomizeAvatar = ResponseSeason2FactoryUpdateCustomizeAvatar
|
||||
|
||||
RequestFactoryRenameBefore = RequestSeason2FactoryRenameBefore
|
||||
ResponseFactoryRenameBefore = ResponseSeason2FactoryRenameBefore
|
||||
|
||||
RequestFactoryRenameResult = RequestSeason2FactoryRenameResult
|
||||
ResponseFactoryRenameResult = ResponseSeason2FactoryRenameResult
|
||||
|
||||
RequestFactoryNumberLotteryBefore = RequestSeason2FactoryNumberLotteryBefore
|
||||
ResponseFactoryNumberLotteryBefore = ResponseSeason2FactoryNumberLotteryBefore
|
||||
|
||||
RequestFactoryNumberLotteryResult = RequestSeason2FactoryNumberLotteryResult
|
||||
ResponseFactoryNumberLotteryResult = ResponseSeason2FactoryNumberLotteryResult
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason2Config,
|
||||
user_service: IDACSeason2UserService,
|
||||
factory_service: IDACSeason2FactoryService,
|
||||
) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.server_cfg = server_cfg
|
||||
self.game_cfg = game_cfg
|
||||
self.game = IDACConstants.GAME_CODE
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||
self.logger = logging.getLogger("idac")
|
||||
|
||||
self.user_service = user_service
|
||||
self.factory_service = factory_service
|
||||
|
||||
async def handle_get_car_data_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactoryGetCarData:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactoryGetCarData.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
car_dto_list = await self.factory_service.get_profile_cars(user_id)
|
||||
|
||||
factory_car_dto_list = [
|
||||
ResponseSeason2FactoryGetCarData.FactoryCarData(
|
||||
**car.model_dump(), id=user_id
|
||||
)
|
||||
for car in car_dto_list
|
||||
]
|
||||
|
||||
return self.ResponseFactoryGetCarData(car_data=factory_car_dto_list)
|
||||
|
||||
async def handle_avatar_gacha_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactoryAvatarGachaResult:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactoryAvatarGachaResult.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
# save user's stock
|
||||
await self.user_service.put_profile_stock(
|
||||
user_id, StockData.model_validate(req.stock_obj)
|
||||
)
|
||||
|
||||
# deduct used tickets
|
||||
ticket_dto = await self.user_service.get_profile_ticket(
|
||||
user_id, RewardCategoryEnum.AVATAR_TOKEN
|
||||
)
|
||||
ticket_dto.ticket_cnt -= req.use_ticket_cnt
|
||||
# save tickets
|
||||
await self.user_service.put_profile_tickets(
|
||||
user_id, [TicketDto.model_validate(ticket_dto)]
|
||||
)
|
||||
|
||||
ticket_dto_list = await self.user_service.get_profile_tickets(user_id)
|
||||
|
||||
return self.ResponseFactoryAvatarGachaResult(ticket_data=ticket_dto_list)
|
||||
|
||||
async def handle_buy_car_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactoryBuyCarResult:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactoryBuyCarResult.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
# get the user's cars
|
||||
car_dto_list = await self.factory_service.get_profile_cars(user_id)
|
||||
|
||||
new_car_dto = CarDto(
|
||||
car_id=req.car_id,
|
||||
style_car_id=req.style_car_id,
|
||||
color=req.color,
|
||||
bureau=req.bureau,
|
||||
kana=req.kana,
|
||||
s_no=req.s_no,
|
||||
l_no=req.l_no,
|
||||
car_flag=req.car_flag,
|
||||
tune_point=req.tune_point,
|
||||
tune_level=16 if req.use_ticket else 1,
|
||||
tune_parts=req.tune_parts,
|
||||
color_stock_list=req.color_stock_list,
|
||||
color_stock_new_list=req.color_stock_new_list,
|
||||
parts_stock_list=req.parts_stock_list,
|
||||
parts_stock_new_list=req.parts_stock_new_list,
|
||||
parts_set_equip_list=req.parts_set_equip_list,
|
||||
parts_list=req.parts_list,
|
||||
equip_parts_count=req.equip_parts_count,
|
||||
pickup_seq=next(
|
||||
(
|
||||
car.pickup_seq
|
||||
for car in req.pickup_on_car_ids
|
||||
if car.style_car_id == req.style_car_id
|
||||
),
|
||||
0,
|
||||
),
|
||||
purchase_seq=max((car.purchase_seq for car in car_dto_list), default=0) + 1,
|
||||
)
|
||||
|
||||
# add the new car dto to the list
|
||||
car_dto_list.append(new_car_dto)
|
||||
|
||||
# save new car
|
||||
await self.factory_service.put_profile_car(user_id, new_car_dto)
|
||||
|
||||
# save favorite cars (pickup on)
|
||||
for pickup_car in req.pickup_on_car_ids:
|
||||
if pickup_car.style_car_id != req.style_car_id:
|
||||
await self.factory_service.set_car_favorite(
|
||||
user_id,
|
||||
pickup_car.style_car_id,
|
||||
pickup_car.pickup_seq,
|
||||
car_dto_list,
|
||||
)
|
||||
|
||||
# remove from favorites (pickup off)
|
||||
for pickup_off_car in req.pickup_off_car_ids:
|
||||
await self.factory_service.unset_car_favorite(
|
||||
user_id, pickup_off_car.style_car_id, car_dto_list
|
||||
)
|
||||
|
||||
# handle full tune ticket
|
||||
if req.use_ticket:
|
||||
ticket_dto = await self.user_service.get_profile_ticket(
|
||||
user_id, RewardCategoryEnum.FULLTUNE_TICKET
|
||||
)
|
||||
ticket_dto.ticket_cnt -= 1
|
||||
await self.user_service.put_profile_tickets(user_id, [ticket_dto])
|
||||
|
||||
ticket_dto_list = await self.user_service.get_profile_tickets(user_id)
|
||||
|
||||
# get the user profile for total_car_parts_count
|
||||
profile_dto = await self.user_service.get_profile(user_id)
|
||||
|
||||
# calculate the different counts for cars
|
||||
fulltune_count = self.factory_service.calculate_fulltune_count(car_dto_list)
|
||||
|
||||
# those are needed for titles
|
||||
car_layout_count = self.factory_service.calculate_car_layout_count(car_dto_list)
|
||||
car_style_count = self.factory_service.calculate_car_style_count(car_dto_list)
|
||||
|
||||
return self.ResponseFactoryBuyCarResult(
|
||||
ticket_data=ticket_dto_list,
|
||||
fulltune_count=fulltune_count,
|
||||
total_car_parts_count=profile_dto.total_car_parts_count,
|
||||
car_layout_count=car_layout_count,
|
||||
car_style_count=car_style_count,
|
||||
)
|
||||
|
||||
async def handle_save_favorite_car_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactorySaveFavoriteCar:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactorySaveFavoriteCar.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
# get the user's car
|
||||
car_dto_list = await self.factory_service.get_profile_cars(user_id)
|
||||
|
||||
# save favorite cars (pickup on)
|
||||
for pickup_car in req.pickup_on_car_ids:
|
||||
await self.factory_service.set_car_favorite(
|
||||
user_id, pickup_car.style_car_id, pickup_car.pickup_seq, car_dto_list
|
||||
)
|
||||
|
||||
# remove from favorites (pickup off)
|
||||
for pickup_off_car in req.pickup_off_car_ids:
|
||||
await self.factory_service.unset_car_favorite(
|
||||
user_id, pickup_off_car.style_car_id, car_dto_list
|
||||
)
|
||||
|
||||
return self.ResponseFactorySaveFavoriteCar()
|
||||
|
||||
async def handle_update_customize_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactoryUpdateCustomizeResult:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactoryUpdateCustomizeResult.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
# save total_car_parts_count
|
||||
self.user_service.put_profile(
|
||||
user_id, ProfileDto(total_car_parts_count=req.total_car_parts_count)
|
||||
)
|
||||
|
||||
# get the user's car
|
||||
car_dto_list = await self.factory_service.get_profile_cars(user_id)
|
||||
|
||||
# Find the car being customized
|
||||
car_dto = self.factory_service._get_used_car(req.style_car_id, car_dto_list)
|
||||
|
||||
if not car_dto:
|
||||
raise ValueError(f"Car {req.style_car_id} not found for user {user_id}")
|
||||
|
||||
car_dto.color = req.color
|
||||
car_dto.color_stock_list = req.color_stock_list
|
||||
car_dto.color_stock_new_list = req.color_stock_new_list
|
||||
car_dto.parts_stock_list = req.parts_stock_list
|
||||
car_dto.parts_stock_new_list = req.parts_stock_new_list
|
||||
car_dto.parts_set_equip_list = req.parts_set_equip_list
|
||||
car_dto.parts_list = req.parts_list
|
||||
car_dto.equip_parts_count = req.equip_parts_count
|
||||
|
||||
# save updated car
|
||||
await self.factory_service.put_profile_car(
|
||||
user_id, CarDto.model_validate(car_dto)
|
||||
)
|
||||
|
||||
# save tickets
|
||||
await self.user_service.put_profile_tickets(
|
||||
user_id, [TicketDto.model_validate(ticket) for ticket in req.ticket_data]
|
||||
)
|
||||
|
||||
return self.ResponseFactoryUpdateCustomizeResult()
|
||||
|
||||
async def handle_update_multiple_customize_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactoryUpdateMultipleCustomizeResult:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactoryUpdateMultipleCustomizeResult.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
# save total_car_parts_count
|
||||
self.user_service.put_profile(
|
||||
user_id, ProfileDto(total_car_parts_count=req.total_car_parts_count)
|
||||
)
|
||||
|
||||
# get the user's car list
|
||||
car_dto_list = await self.factory_service.get_profile_cars(user_id)
|
||||
|
||||
# update each car's customization
|
||||
for factory_car in req.car_list:
|
||||
car_dto = self.factory_service._get_used_car(
|
||||
factory_car.style_car_id, car_dto_list
|
||||
)
|
||||
|
||||
if not car_dto:
|
||||
raise ValueError(
|
||||
f"Car {factory_car.style_car_id} not found for user {user_id}"
|
||||
)
|
||||
|
||||
# update customization fields
|
||||
car_dto.color = factory_car.color
|
||||
car_dto.color_stock_list = factory_car.color_stock_list
|
||||
car_dto.color_stock_new_list = factory_car.color_stock_new_list
|
||||
car_dto.parts_stock_list = factory_car.parts_stock_list
|
||||
car_dto.parts_stock_new_list = factory_car.parts_stock_new_list
|
||||
car_dto.parts_set_equip_list = factory_car.parts_set_equip_list
|
||||
car_dto.parts_list = factory_car.parts_list
|
||||
car_dto.equip_parts_count = factory_car.equip_parts_count
|
||||
|
||||
# save updated car
|
||||
await self.factory_service.put_profile_car(
|
||||
user_id, CarDto.model_validate(car_dto)
|
||||
)
|
||||
|
||||
# save tickets
|
||||
await self.user_service.put_profile_tickets(
|
||||
user_id, [TicketDto.model_validate(ticket) for ticket in req.ticket_data]
|
||||
)
|
||||
|
||||
return self.ResponseFactoryUpdateMultipleCustomizeResult()
|
||||
|
||||
async def handle_update_customize_user_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactoryUpdateCustomizeUser:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactoryUpdateCustomizeUser.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
# get the user's profile
|
||||
profile_dto = await self.user_service.get_profile(user_id)
|
||||
|
||||
# update profile fields
|
||||
profile_dto.mytitle_id = req.mytitle_id
|
||||
profile_dto.mytitle_efffect_id = req.mytitle_efffect_id
|
||||
profile_dto.papercup_id = req.papercup_id
|
||||
profile_dto.tachometer_id = req.tachometer_id
|
||||
profile_dto.aura_id = req.aura_id
|
||||
profile_dto.aura_color_id = req.aura_color_id
|
||||
profile_dto.aura_line_id = req.aura_line_id
|
||||
profile_dto.bgm_id = req.bgm_id
|
||||
profile_dto.keyholder_id = req.keyholder_id
|
||||
profile_dto.start_menu_bg_id = req.start_menu_bg_id
|
||||
profile_dto.stamp_key_assign_0 = req.stamp_key_assign_0
|
||||
profile_dto.stamp_key_assign_1 = req.stamp_key_assign_1
|
||||
profile_dto.stamp_key_assign_2 = req.stamp_key_assign_2
|
||||
profile_dto.stamp_key_assign_3 = req.stamp_key_assign_3
|
||||
|
||||
# save user's profile customization
|
||||
await self.user_service.put_profile(
|
||||
user_id, ProfileDto.model_validate(profile_dto)
|
||||
)
|
||||
|
||||
# save user's stock
|
||||
await self.user_service.put_profile_stock(
|
||||
user_id, StockData.model_validate(req.stock_obj)
|
||||
)
|
||||
|
||||
return self.ResponseFactoryUpdateCustomizeUser()
|
||||
|
||||
async def handle_update_customize_avatar_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactoryUpdateCustomizeAvatar:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactoryUpdateCustomizeAvatar.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
# save user's stock
|
||||
await self.user_service.put_profile_stock(
|
||||
user_id, StockData.model_validate(req.stock_obj)
|
||||
)
|
||||
|
||||
# save user's avatar
|
||||
await self.user_service.put_profile_avatar(
|
||||
user_id, AvatarData.model_validate(req.avatar_obj)
|
||||
)
|
||||
|
||||
return self.ResponseFactoryUpdateCustomizeAvatar()
|
||||
|
||||
async def handle_rename_before_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactoryRenameBefore:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactoryRenameBefore.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
profile_dto = await self.user_service.get_profile(user_id)
|
||||
|
||||
# set the profile name change category to credit retry
|
||||
if profile_dto.name_change_category == NameChangeCategoryEnum.DENIED:
|
||||
return self.ResponseFactoryRenameBefore(
|
||||
status_code=StatusCodeEnum.BAD_REQUEST
|
||||
)
|
||||
|
||||
profile_dto.name_change_category = NameChangeCategoryEnum.CREDIT_RETRY
|
||||
await self.user_service.put_profile(user_id, profile_dto)
|
||||
|
||||
return self.ResponseFactoryRenameBefore()
|
||||
|
||||
async def handle_rename_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactoryRenameResult:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactoryRenameResult.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
profile_dto = await self.user_service.get_profile(user_id)
|
||||
|
||||
if profile_dto.name_change_category == NameChangeCategoryEnum.DENIED:
|
||||
return self.ResponseFactoryRenameBefore(
|
||||
status_code=StatusCodeEnum.BAD_REQUEST
|
||||
)
|
||||
|
||||
# save user's new name and name change category to denied
|
||||
profile_dto.username = req.username
|
||||
profile_dto.name_change_category = NameChangeCategoryEnum.DENIED
|
||||
await self.user_service.put_profile(user_id, profile_dto)
|
||||
|
||||
return self.ResponseFactoryRenameResult()
|
||||
|
||||
async def handle_number_lottery_before_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactoryNumberLotteryBefore:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactoryNumberLotteryBefore.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
lottery_info = await self.factory_service.calculate_lottery_info(user_id)
|
||||
|
||||
return self.ResponseFactoryNumberLotteryBefore(lottery_info=lottery_info)
|
||||
|
||||
async def handle_number_lottery_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseFactoryNumberLotteryResult:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestFactoryNumberLotteryResult.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
# get lottery data
|
||||
lottery_dto = await self.factory_service.get_lottery(user_id)
|
||||
if not lottery_dto:
|
||||
lottery_dto = LotteryDto(
|
||||
create_date=datetime.now(),
|
||||
saved_value=0,
|
||||
lottery_count=req.lottery_count,
|
||||
)
|
||||
|
||||
if req.win_number != 10000:
|
||||
shifted = req.win_number // 1111
|
||||
lottery_dto.saved_value += 1 << shifted
|
||||
|
||||
if lottery_dto and datetime.now() - lottery_dto.create_date > timedelta(days=1):
|
||||
lottery_dto.create_date = datetime.now()
|
||||
|
||||
await self.factory_service.put_lottery(
|
||||
user_id, LotteryDto.model_validate(lottery_dto)
|
||||
)
|
||||
|
||||
# get the user's car
|
||||
car_dto_list = await self.factory_service.get_profile_cars(user_id)
|
||||
# find the car for lottery
|
||||
car_dto = self.factory_service._get_used_car(req.style_car_id, car_dto_list)
|
||||
|
||||
if req.l_no != 10000 and req.isEnd == 1:
|
||||
car_dto.l_no = req.l_no
|
||||
await self.factory_service.put_profile_car(
|
||||
user_id, CarDto.model_validate(car_dto)
|
||||
)
|
||||
|
||||
if req.ticket_data:
|
||||
await self.user_service.put_profile_tickets(
|
||||
user_id,
|
||||
[TicketDto.model_validate(ticket) for ticket in req.ticket_data],
|
||||
)
|
||||
|
||||
# get the user's profile and update profile data
|
||||
profile_dto = ProfileDto(
|
||||
cash=req.cash,
|
||||
total_cash=req.total_cash,
|
||||
dressup_point=req.dressup_point,
|
||||
avatar_point=req.avatar_point,
|
||||
)
|
||||
|
||||
await self.user_service.put_profile(user_id, profile_dto)
|
||||
|
||||
return self.ResponseFactoryNumberLotteryResult()
|
||||
@@ -0,0 +1,50 @@
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.gameconfig.season2_config import IDACSeason2Config
|
||||
from titles.idac.model.season2.common_model import RequestSeason2UserHeaders
|
||||
from titles.idac.model.season2.log_model import (
|
||||
RequestSeason2LogSavePlayLog,
|
||||
ResponseSeason2LogSavePlayLog,
|
||||
RequestSeason2LogSaveEndLog,
|
||||
ResponseSeason2LogSaveEndLog,
|
||||
)
|
||||
|
||||
|
||||
class IDACSeason2LogController:
|
||||
RequestLogSavePlayLog = RequestSeason2LogSavePlayLog
|
||||
ResponseLogSavePlayLog = ResponseSeason2LogSavePlayLog
|
||||
|
||||
RequestLogSaveEndLog = RequestSeason2LogSaveEndLog
|
||||
ResponseLogSaveEndLog = ResponseSeason2LogSaveEndLog
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason2Config,
|
||||
) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.server_cfg = server_cfg
|
||||
self.game_cfg = game_cfg
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||
|
||||
async def handle_save_play_log_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseLogSavePlayLog:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestLogSavePlayLog.model_validate(data)
|
||||
|
||||
return self.ResponseLogSavePlayLog()
|
||||
|
||||
async def handle_save_end_log_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> RequestLogSaveEndLog:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestLogSaveEndLog.model_validate(data)
|
||||
|
||||
return self.ResponseLogSaveEndLog()
|
||||
@@ -0,0 +1,128 @@
|
||||
import logging
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.gameconfig.season2_config import IDACSeason2Config
|
||||
from titles.idac.model.common_model import LockResultEnum
|
||||
from titles.idac.model.season2.common_model import RequestSeason2Headers
|
||||
from titles.idac.model.season2.login_model import *
|
||||
from titles.idac.service.season2.login_service import IDACSeason2LoginService
|
||||
from titles.idac.service.season2.user_service import IDACSeason2UserService
|
||||
|
||||
|
||||
class IDACSeason2LoginController:
|
||||
RequestLoginCheckLock = RequestSeason2LoginCheckLock
|
||||
ResponseLoginCheckLock = ResponseSeason2LoginCheckLock
|
||||
|
||||
RequestLoginUnLock = RequestSeason2LoginUnLock
|
||||
ResponseLoginUnLock = ResponseSeason2LoginUnLock
|
||||
|
||||
RequestLoginReLock = RequestSeason2LoginReLock
|
||||
ResponseLoginReLock = ResponseSeason2LoginReLock
|
||||
|
||||
RequestLoginGuestPlay = RequestSeason2LoginGuestPlay
|
||||
ResponseLoginGuestPlay = ResponseSeason2LoginGuestPlay
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason2Config,
|
||||
login_service: IDACSeason2LoginService,
|
||||
user_service: IDACSeason2UserService,
|
||||
) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.server_cfg = server_cfg
|
||||
self.game_config = game_cfg
|
||||
|
||||
self.game = IDACConstants.GAME_CODE
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||
self.logger = logging.getLogger("idac")
|
||||
|
||||
self.login_service = login_service
|
||||
self.user_service = user_service
|
||||
|
||||
def _is_valid_version(self, db_version: str, cl_version: str) -> bool:
|
||||
if len(db_version) < 7 or len(cl_version) < 7:
|
||||
return False
|
||||
|
||||
# convert the trings to int and compare
|
||||
db_version = int(db_version.replace(".", "")[:7])
|
||||
cl_version = int(cl_version.replace(".", "")[:7])
|
||||
|
||||
return cl_version >= db_version
|
||||
|
||||
async def handle_check_lock_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason2LoginCheckLock:
|
||||
req = self.RequestLoginCheckLock.model_validate(data)
|
||||
req_headers = RequestSeason2Headers.model_validate(headers)
|
||||
|
||||
user_id = req.id
|
||||
|
||||
lock_result = LockResultEnum.IN_USE
|
||||
is_new_player = 0
|
||||
|
||||
# check that the user_id from access_code matches the user_id
|
||||
if await self.login_service.is_valid_user_id(user_id, req.accesscode):
|
||||
lock_result = LockResultEnum.OK
|
||||
|
||||
# check if an IDAC profile already exists
|
||||
p = await self.user_service.get_profile(user_id)
|
||||
is_new_player = 1 if p is None else 0
|
||||
|
||||
db_version = p.device_version if p is not None else "1.00.00"
|
||||
|
||||
# check if the client version is valid
|
||||
if not self._is_valid_version(db_version, req_headers.device_version):
|
||||
lock_result = LockResultEnum.NEWER_DATA
|
||||
else:
|
||||
user_id = ""
|
||||
|
||||
takeover_data = []
|
||||
|
||||
return self.ResponseLoginCheckLock(
|
||||
lock_result=lock_result,
|
||||
lock_date=datetime.now(),
|
||||
# TODO?
|
||||
daily_play=0,
|
||||
session=f"{user_id}",
|
||||
shared_security_key="a",
|
||||
session_procseq=1,
|
||||
new_player=is_new_player,
|
||||
server_status=1,
|
||||
takeover_data=takeover_data,
|
||||
)
|
||||
|
||||
async def handle_un_lock_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason2LoginUnLock:
|
||||
req = self.RequestLoginUnLock.model_validate(data)
|
||||
req_headers = RequestSeason2Headers.model_validate(headers)
|
||||
|
||||
return self.ResponseLoginUnLock(lock_result=LockResultEnum.OK)
|
||||
|
||||
async def handle_re_lock_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason2LoginReLock:
|
||||
req = self.RequestLoginReLock.model_validate(data)
|
||||
req_headers = RequestSeason2Headers.model_validate(headers)
|
||||
|
||||
return self.ResponseLoginReLock(
|
||||
lock_result=LockResultEnum.OK, lock_date=int(datetime.now().timestamp())
|
||||
)
|
||||
|
||||
async def handle_guest_play_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason2LoginGuestPlay:
|
||||
req = self.RequestLoginGuestPlay.model_validate(data)
|
||||
req_headers = RequestSeason2Headers.model_validate(headers)
|
||||
|
||||
return self.ResponseLoginGuestPlay(
|
||||
session="-1", shared_security_key="Guest", session_procseq=0
|
||||
)
|
||||
@@ -0,0 +1,66 @@
|
||||
import logging
|
||||
|
||||
from typing import Dict
|
||||
|
||||
from pydantic_core import CoreConfig
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.gameconfig.season2_config import IDACSeason2Config
|
||||
from titles.idac.model.season2.common_model import *
|
||||
from titles.idac.model.season2.user_model import *
|
||||
from titles.idac.model.season2.theory_model import *
|
||||
from titles.idac.service.season2.factory_service import IDACSeason2FactoryService
|
||||
from titles.idac.service.season2.theory_service import IDACSeason2TheoryService
|
||||
from titles.idac.service.season2.user_service import IDACSeason2UserService
|
||||
|
||||
|
||||
class IDACSeason2TheoryController:
|
||||
RequestTheoryMatching = RequestSeason2TheoryMatching
|
||||
ResponseTheoryMatching = ResponseSeason2TheoryMatching
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason2Config,
|
||||
user_service: IDACSeason2UserService,
|
||||
theory_service: IDACSeason2TheoryService,
|
||||
factory_service: IDACSeason2FactoryService,
|
||||
) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.server_cfg = server_cfg
|
||||
self.game_cfg = game_cfg
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||
self.game = IDACConstants.GAME_CODE
|
||||
self.logger = logging.getLogger("idac")
|
||||
|
||||
self.user_service = user_service
|
||||
self.theory_service = theory_service
|
||||
self.factory_service = factory_service
|
||||
|
||||
async def handle_matching_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseTheoryMatching:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestTheoryMatching.model_validate(data)
|
||||
|
||||
power_match: list[Season2PowerMatch] = []
|
||||
user_id = req_headers.session
|
||||
|
||||
auto_match = await self.theory_service.generate_auto_match(
|
||||
user_id, req.grade, req.course_id, req_headers.device_version
|
||||
)
|
||||
|
||||
# only get the power_match if requested
|
||||
if req.powerhouse_lv > 0:
|
||||
power_match = await self.theory_service.generate_power_match(
|
||||
user_id, req.grade, req.course_id, req.powerhouse_lv, req_headers.device_version
|
||||
)
|
||||
|
||||
return self.ResponseTheoryMatching(
|
||||
rival_data=ResponseSeason2TheoryMatching.RivalData(
|
||||
auto_match=auto_match,
|
||||
power_match=power_match,
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,107 @@
|
||||
import logging
|
||||
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.model.season2.common_model import RequestSeason2UserHeaders
|
||||
from titles.idac.model.season2.time_trial_model import (
|
||||
RequestSeason2TimeTrialGetBestRecordPreTA,
|
||||
ResponseSeason2TimeTrialGetBestRecordPreTA,
|
||||
)
|
||||
from titles.idac.service.season2.time_trial_service import *
|
||||
|
||||
|
||||
class IDACSeason2TimeTrialController:
|
||||
RequestTimeTrialGetBestRecordPreTA = RequestSeason2TimeTrialGetBestRecordPreTA
|
||||
ResponseTimeTrialGetBestRecordPreTA = ResponseSeason2TimeTrialGetBestRecordPreTA
|
||||
|
||||
RequestTimeTrialGetBestRecordPreRace = RequestSeason2TimeTrialGetBestRecordPreRace
|
||||
ResponseTimeTrialGetBestRecordPreRace = ResponseSeason2TimeTrialGetBestRecordPreRace
|
||||
|
||||
RequestTimeTrialGetBestRecordPreBattle = (
|
||||
RequestSeason2TimeTrialGetBestRecordPreBattle
|
||||
)
|
||||
ResponseTimeTrialGetBestRecordPreBattle = (
|
||||
ResponseSeason2TimeTrialGetBestRecordPreBattle
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason2Config,
|
||||
time_trial_service: IDACSeason2TimeTrialService,
|
||||
) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.server_cfg = server_cfg
|
||||
self.game_cfg = game_cfg
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||
self.logger = logging.getLogger("idac")
|
||||
|
||||
self.time_trial_service = time_trial_service
|
||||
|
||||
async def handle_get_best_record_pre_ta_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseTimeTrialGetBestRecordPreTA:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestTimeTrialGetBestRecordPreTA.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
course_mybest_data = await self.time_trial_service.get_course_mybest_data(
|
||||
user_id
|
||||
)
|
||||
|
||||
style_car_id_list = [car.style_car_id for car in req.car_ids]
|
||||
|
||||
course_pickup_car_best_data = (
|
||||
await self.time_trial_service.get_course_pickup_car_best_data(
|
||||
user_id, style_car_id_list
|
||||
)
|
||||
)
|
||||
|
||||
return self.ResponseTimeTrialGetBestRecordPreTA(
|
||||
course_mybest_data=course_mybest_data,
|
||||
course_pickup_car_best_data=course_pickup_car_best_data,
|
||||
)
|
||||
|
||||
async def handle_get_best_record_pre_race_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseTimeTrialGetBestRecordPreRace:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestTimeTrialGetBestRecordPreRace.model_validate(data)
|
||||
|
||||
style_car_id_list = [car.style_car_id for car in req.car_ids]
|
||||
course_id = req.course_id
|
||||
|
||||
course_car_best_data = await self.time_trial_service.get_course_car_best_data(
|
||||
course_id, style_car_id_list
|
||||
)
|
||||
|
||||
course_best_data = await self.time_trial_service.get_course_best_data(course_id)
|
||||
|
||||
return self.ResponseTimeTrialGetBestRecordPreRace(
|
||||
course_car_best_data=course_car_best_data, course_best_data=course_best_data
|
||||
)
|
||||
|
||||
async def handle_get_best_record_pre_battle_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseTimeTrialGetBestRecordPreRace:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestTimeTrialGetBestRecordPreBattle.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
style_car_id_list = [car.style_car_id for car in req.car_ids]
|
||||
|
||||
course_pickup_car_best_data = (
|
||||
await self.time_trial_service.get_course_pickup_car_best_data(
|
||||
user_id, style_car_id_list
|
||||
)
|
||||
)
|
||||
|
||||
return self.ResponseTimeTrialGetBestRecordPreBattle(
|
||||
course_pickup_car_best_data=course_pickup_car_best_data
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
from core.config import CoreConfig
|
||||
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.gameconfig.season3_config import IDACSeason3Config
|
||||
from titles.idac.controller.season2.advertise_controller import (
|
||||
IDACSeason2AdvertiseController,
|
||||
)
|
||||
from titles.idac.service.season3.advertise_service import IDACSeason3AdvertiseService
|
||||
|
||||
|
||||
class IDACSeason3AdvertiseController(IDACSeason2AdvertiseController):
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason3Config,
|
||||
advertise_service: IDACSeason3AdvertiseService,
|
||||
) -> None:
|
||||
super().__init__(core_cfg, server_cfg, game_cfg, advertise_service)
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
@@ -0,0 +1,17 @@
|
||||
from core.config import CoreConfig
|
||||
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.controller.season2.alive_controller import IDACSeason2AliveController
|
||||
from titles.idac.gameconfig.season3_config import IDACSeason3Config
|
||||
|
||||
|
||||
class IDACSeason3AliveController(IDACSeason2AliveController):
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason3Config,
|
||||
) -> None:
|
||||
super().__init__(core_cfg, server_cfg, game_cfg)
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
@@ -0,0 +1,39 @@
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import CollaboEnum, IDACConstants
|
||||
from titles.idac.controller.season2.boot_controller import IDACSeason2BootController
|
||||
from titles.idac.gameconfig.season3_config import IDACSeason3Config
|
||||
from titles.idac.model.season3.boot_model import ResponseSeason3BootGetConfigData
|
||||
|
||||
|
||||
class IDACSeason3BootController(IDACSeason2BootController):
|
||||
ResponseBootGetConfigData = ResponseSeason3BootGetConfigData
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason3Config,
|
||||
) -> None:
|
||||
super().__init__(core_cfg, server_cfg, game_cfg)
|
||||
|
||||
self.game_cfg = game_cfg
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
|
||||
async def handle_get_config_data_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3BootGetConfigData:
|
||||
resp: ResponseSeason3BootGetConfigData = (
|
||||
await super().handle_get_config_data_request(data, headers)
|
||||
)
|
||||
|
||||
resp.collabo_id = CollaboEnum.HOT_VERSION
|
||||
resp.theory_open_version = "2.00.00"
|
||||
resp.league_rewards = self.game_cfg.league_reward_info
|
||||
resp.league_flag = 0 # 0 = open league?
|
||||
|
||||
return resp
|
||||
@@ -0,0 +1,25 @@
|
||||
from core.config import CoreConfig
|
||||
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.controller.season2.factory_controller import (
|
||||
IDACSeason2FactoryController,
|
||||
)
|
||||
from titles.idac.gameconfig.season3_config import IDACSeason3Config
|
||||
from titles.idac.model.season3.user_model import *
|
||||
from titles.idac.service.season3.factory_service import IDACSeason3FactoryService
|
||||
from titles.idac.service.season3.user_service import IDACSeason3UserService
|
||||
|
||||
|
||||
class IDACSeason3FactoryController(IDACSeason2FactoryController):
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason3Config,
|
||||
user_service: IDACSeason3UserService,
|
||||
factory_service: IDACSeason3FactoryService,
|
||||
) -> None:
|
||||
super().__init__(core_cfg, server_cfg, game_cfg, user_service, factory_service)
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
@@ -0,0 +1,18 @@
|
||||
from core.config import CoreConfig
|
||||
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.controller.season2.log_controller import IDACSeason2LogController
|
||||
from titles.idac.gameconfig.season3_config import IDACSeason3Config
|
||||
|
||||
|
||||
class IDACSeason3LogController(IDACSeason2LogController):
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason3Config,
|
||||
) -> None:
|
||||
super().__init__(core_cfg, server_cfg, game_cfg)
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
@@ -0,0 +1,22 @@
|
||||
from core.config import CoreConfig
|
||||
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.controller.season2.login_controller import IDACSeason2LoginController
|
||||
from titles.idac.gameconfig.season3_config import IDACSeason3Config
|
||||
from titles.idac.service.season3.login_service import IDACSeason3LoginService
|
||||
from titles.idac.service.season3.user_service import IDACSeason3UserService
|
||||
|
||||
|
||||
class IDACSeason3LoginController(IDACSeason2LoginController):
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason3Config,
|
||||
login_service: IDACSeason3LoginService,
|
||||
user_service: IDACSeason3UserService,
|
||||
) -> None:
|
||||
super().__init__(core_cfg, server_cfg, game_cfg, login_service, user_service)
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
@@ -0,0 +1,330 @@
|
||||
import logging
|
||||
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.gameconfig.season3_config import IDACSeason3Config
|
||||
from titles.idac.model.season2.common_model import RequestSeason2UserHeaders
|
||||
from titles.idac.model.season3.common_model import *
|
||||
from titles.idac.model.season3.result_model import *
|
||||
from titles.idac.model.season3.team_model import *
|
||||
from titles.idac.service.season3.team_service import IDACSeason3TeamService
|
||||
from titles.idac.service.season3.user_service import IDACSeason3UserService
|
||||
|
||||
|
||||
class IDACSeason3TeamController:
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason3Config,
|
||||
user_service: IDACSeason3UserService,
|
||||
team_service: IDACSeason3TeamService,
|
||||
) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
self.server_cfg = server_cfg
|
||||
self.game_cfg = game_cfg
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
self.logger = logging.getLogger("idac")
|
||||
|
||||
self.user_service = user_service
|
||||
self.team_service = team_service
|
||||
|
||||
async def handle_create_team_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3CreateTeam:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = RequestSeason3CreateTeam.model_validate(data)
|
||||
|
||||
user_id = req_headers.session
|
||||
country = req_headers.a_country
|
||||
store_id = req_headers.a_store
|
||||
|
||||
team_info = await self.team_service.create_team(
|
||||
user_id, req.team_name, country, store_id, req.auto_invite_flag
|
||||
)
|
||||
|
||||
return ResponseSeason3CreateTeam(
|
||||
team_info=team_info,
|
||||
auto_invite_flag=req.auto_invite_flag,
|
||||
)
|
||||
|
||||
async def handle_left_team_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3LeftTeam:
|
||||
user_id = headers["session"]
|
||||
|
||||
left_result = await self.team_service.leave_team(user_id)
|
||||
|
||||
return ResponseSeason3LeftTeam(
|
||||
left_status=0 if left_result else 1,
|
||||
)
|
||||
|
||||
async def handle_pre_join_team_request(self, data: Dict, headers: Dict):
|
||||
req = RequestSeason3PreJoinTeam.model_validate(data)
|
||||
|
||||
team_info = await self.team_service.pre_join_team(req.password)
|
||||
|
||||
if not team_info:
|
||||
# failed to find team for the given D number (password)
|
||||
return ResponseSeason3PreJoinTeam(
|
||||
join_status=JoinStatusEnum.INVALID_PASSWORD
|
||||
)
|
||||
|
||||
# check if team already has 10 members
|
||||
team_member_list = await self.team_service.get_all_team_member_dto(
|
||||
team_info.team_id
|
||||
)
|
||||
if len(team_member_list) >= 10:
|
||||
return ResponseSeason3PreJoinTeam(join_status=JoinStatusEnum.TEAM_FULL)
|
||||
|
||||
# successfully found team
|
||||
return ResponseSeason3PreJoinTeam(
|
||||
join_status=JoinStatusEnum.SUCCESS,
|
||||
team_info=team_info,
|
||||
)
|
||||
|
||||
async def handle_join_team_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3JoinTeam:
|
||||
req = RequestSeason3JoinTeam.model_validate(data)
|
||||
user_id = headers["session"]
|
||||
|
||||
response_prejoin = await self.handle_pre_join_team_request(data, headers)
|
||||
if response_prejoin.join_status != JoinStatusEnum.SUCCESS:
|
||||
return ResponseSeason3JoinTeam(
|
||||
join_status=response_prejoin.join_status,
|
||||
)
|
||||
|
||||
team_info = response_prejoin.team_info
|
||||
team_id = team_info.team_id
|
||||
|
||||
join_result = await self.team_service.join_team(user_id, team_id)
|
||||
if not join_result:
|
||||
# failed to join team
|
||||
return ResponseSeason3JoinTeam(join_status=JoinStatusEnum.UNEXPECTED_ERROR)
|
||||
|
||||
league_info = await self.team_service.get_league_info(team_id, user_id)
|
||||
team_member_cars = await self.team_service.get_team_member_cars(user_id)
|
||||
|
||||
# update the tips last team join timestamp
|
||||
await self.user_service.update_tips_team_join(user_id)
|
||||
|
||||
return ResponseSeason3JoinTeam(
|
||||
join_status=JoinStatusEnum.SUCCESS,
|
||||
team_info=team_info,
|
||||
league_info=league_info,
|
||||
team_member_cars=team_member_cars,
|
||||
)
|
||||
|
||||
async def handle_get_team_manage_data_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3GetTeamManageData:
|
||||
user_id = headers["session"]
|
||||
|
||||
# return wrong response if the user is not a leader
|
||||
if not await self.team_service.is_team_leader(user_id):
|
||||
return ResponseSeason3GetTeamManageData(
|
||||
member_data=[],
|
||||
password="",
|
||||
auto_invite_flag=0,
|
||||
)
|
||||
|
||||
team_info_dto = await self.team_service.get_team_info_dto(user_id)
|
||||
member_data = await self.team_service.get_team_member_data(user_id)
|
||||
|
||||
return ResponseSeason3GetTeamManageData(
|
||||
member_data=member_data,
|
||||
password=team_info_dto.password,
|
||||
auto_invite_flag=team_info_dto.auto_invite_flag,
|
||||
)
|
||||
|
||||
async def handle_kick_member_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3TeamKickMember:
|
||||
req = RequestSeason3TeamKickMember.model_validate(data)
|
||||
user_id = headers["session"]
|
||||
|
||||
# this could use teh function is_team_leader but we need to check if the user is a member of the team first
|
||||
leader_member = await self.team_service.get_team_member_dto(user_id)
|
||||
if not leader_member or leader_member.role != TeamRoleEnum.LEADER:
|
||||
# check if the user is a team leader
|
||||
self.logger.warning(
|
||||
f"User {user_id} tried to kick member from team but is not a member or leader!"
|
||||
)
|
||||
return ResponseSeason3TeamKickMember(
|
||||
control_result=1,
|
||||
team_point=0,
|
||||
)
|
||||
|
||||
# check if the user is trying to kick themselves
|
||||
if req.id == user_id:
|
||||
self.logger.warning(
|
||||
f"User {user_id} tried to kick themselves from the team!"
|
||||
)
|
||||
return ResponseSeason3TeamKickMember(
|
||||
control_result=1,
|
||||
team_point=0,
|
||||
)
|
||||
|
||||
# check if the user is in the same team as the leader
|
||||
team_member = await self.team_service.get_team_member_dto(req.id)
|
||||
if not team_member or team_member.team_id != leader_member.team_id:
|
||||
self.logger.warning(
|
||||
f"User {user_id} tried to kick member from team but the member is not in the same team!"
|
||||
)
|
||||
return ResponseSeason3TeamKickMember(
|
||||
control_result=1,
|
||||
team_point=0,
|
||||
)
|
||||
|
||||
left_result = await self.team_service.leave_team(req.id)
|
||||
|
||||
if not left_result:
|
||||
# failed to kick member from team
|
||||
return ResponseSeason3TeamKickMember(
|
||||
control_result=1,
|
||||
team_point=0,
|
||||
)
|
||||
|
||||
# return the new leage info team points
|
||||
league_info = await self.team_service.get_league_info_dto(user_id)
|
||||
|
||||
return ResponseSeason3TeamKickMember(
|
||||
control_result=0,
|
||||
team_point=league_info.league_point,
|
||||
)
|
||||
|
||||
async def handle_change_leader_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponsSeason3TeamChangeLeader:
|
||||
req = RequestSeason3TeamChangeLeader.model_validate(data)
|
||||
user_id = headers["session"]
|
||||
|
||||
if not await self.team_service.is_team_leader(user_id):
|
||||
self.logger.warning(
|
||||
f"User {user_id} tried to change team leader but is not the leader!"
|
||||
)
|
||||
return ResponsSeason3TeamChangeLeader(control_result=1)
|
||||
|
||||
success = await self.team_service.change_team_leader(user_id, req.id)
|
||||
|
||||
return ResponsSeason3TeamChangeLeader(
|
||||
control_result=0 if success else 1,
|
||||
)
|
||||
|
||||
async def handle_change_password_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3TeamChangePassword:
|
||||
user_id = headers["session"]
|
||||
|
||||
if not await self.team_service.is_team_leader(user_id):
|
||||
self.logger.warning(
|
||||
f"User {user_id} tried to change team password but is not the leader!"
|
||||
)
|
||||
return ResponseSeason3TeamChangePassword(
|
||||
control_result=1,
|
||||
password="",
|
||||
)
|
||||
|
||||
password = await self.team_service.change_team_password(user_id)
|
||||
|
||||
return ResponseSeason3TeamChangePassword(
|
||||
control_result=0,
|
||||
password=password,
|
||||
)
|
||||
|
||||
async def handle_get_team_info_data_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3GetTeamInfoData:
|
||||
user_id = headers["session"]
|
||||
|
||||
member_data = await self.team_service.get_team_member_data(user_id)
|
||||
past_league_data = await self.team_service.get_past_league_data(user_id)
|
||||
|
||||
return ResponseSeason3GetTeamInfoData(
|
||||
member_data=member_data,
|
||||
past_league_data=past_league_data,
|
||||
)
|
||||
|
||||
async def handle_get_team_home_data_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3GetTeamHomeData:
|
||||
user_id = headers["session"]
|
||||
|
||||
team_member_cars = await self.team_service.get_team_member_cars(user_id)
|
||||
|
||||
return ResponseSeason3GetTeamHomeData(team_member_cars=team_member_cars)
|
||||
|
||||
async def handle_get_sticker_stock_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3TeamGetTeamLogoStock:
|
||||
user_id = headers["session"]
|
||||
|
||||
sticker_stock = await self.team_service.get_team_logo_stock(user_id)
|
||||
|
||||
return ResponseSeason3TeamGetTeamLogoStock(
|
||||
sticker_stock=sticker_stock,
|
||||
)
|
||||
|
||||
async def handle_update_sticker_data_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3TeamUpdateTeamLogoData:
|
||||
req = RequestSeason3TeamUpdateTeamLogoData.model_validate(data)
|
||||
user_id = headers["session"]
|
||||
|
||||
if not await self.team_service.is_team_leader(user_id):
|
||||
self.logger.warning(
|
||||
f"User {user_id} tried to update team logo but is not the leader!"
|
||||
)
|
||||
return ResponseSeason3TeamUpdateTeamLogoData(
|
||||
control_result=1,
|
||||
sticker_id=0,
|
||||
)
|
||||
|
||||
sticker_id = await self.team_service.update_team_logo(user_id, req.sticker_id)
|
||||
|
||||
return ResponseSeason3TeamUpdateTeamLogoData(
|
||||
control_result=0,
|
||||
sticker_id=sticker_id,
|
||||
)
|
||||
|
||||
async def handle_get_recommend_team_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3TeamGetRecommendTeam:
|
||||
user_id = int(headers["session"])
|
||||
|
||||
recommend_teams = await self.team_service.get_recommend_teams(user_id)
|
||||
|
||||
return ResponseSeason3TeamGetRecommendTeam(
|
||||
join_status=JoinStatusEnum.SUCCESS,
|
||||
recommend_team_list=recommend_teams,
|
||||
)
|
||||
|
||||
async def handle_update_team_option_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseSeason3TeamUpdateTeamOption:
|
||||
req = RequestSeason3TeamUpdateTeamOption.model_validate(data)
|
||||
user_id = headers["session"]
|
||||
|
||||
if not await self.team_service.is_team_leader(user_id):
|
||||
self.logger.warning(
|
||||
f"User {user_id} tried to update team options but is not the leader!"
|
||||
)
|
||||
|
||||
return ResponseSeason3TeamUpdateTeamOption(
|
||||
control_result=1,
|
||||
auto_invite_flag=0,
|
||||
)
|
||||
|
||||
auto_invite_flag = await self.team_service.update_team_option(
|
||||
user_id, req.auto_invite_flag
|
||||
)
|
||||
|
||||
return ResponseSeason3TeamUpdateTeamOption(
|
||||
control_result=0,
|
||||
auto_invite_flag=auto_invite_flag,
|
||||
)
|
||||
@@ -0,0 +1,80 @@
|
||||
from typing import Dict
|
||||
|
||||
from pydantic_core import CoreConfig
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.controller.season2.theory_controller import IDACSeason2TheoryController
|
||||
from titles.idac.gameconfig.season3_config import IDACSeason3Config
|
||||
from titles.idac.model.season3.common_model import RequestSeason3UserHeaders
|
||||
from titles.idac.model.season3.theory_model import (
|
||||
RequestSeason3TheoryMatching,
|
||||
ResponseSeason3TheoryMatching,
|
||||
Season3PowerMatch,
|
||||
)
|
||||
from titles.idac.service.season3.factory_service import IDACSeason3FactoryService
|
||||
from titles.idac.service.season3.team_service import IDACSeason3TeamService
|
||||
from titles.idac.service.season3.theory_service import IDACSeason3TheoryService
|
||||
from titles.idac.service.season3.user_service import IDACSeason3UserService
|
||||
|
||||
|
||||
class IDACSeason3TheoryController(IDACSeason2TheoryController):
|
||||
RequestTheoryMatching = RequestSeason3TheoryMatching
|
||||
ResponseTheoryMatching = ResponseSeason3TheoryMatching
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason3Config,
|
||||
user_service: IDACSeason3UserService,
|
||||
theory_service: IDACSeason3TheoryService,
|
||||
factory_service: IDACSeason3FactoryService,
|
||||
team_service: IDACSeason3TeamService,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
core_cfg,
|
||||
server_cfg,
|
||||
game_cfg,
|
||||
user_service,
|
||||
theory_service,
|
||||
factory_service,
|
||||
)
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
|
||||
self.team_service = team_service
|
||||
|
||||
async def handle_matching_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseTheoryMatching:
|
||||
req_headers = RequestSeason3UserHeaders.model_validate(headers)
|
||||
req = self.RequestTheoryMatching.model_validate(data)
|
||||
|
||||
resp: ResponseSeason3TheoryMatching = await super().handle_matching_request(
|
||||
data, headers
|
||||
)
|
||||
|
||||
# iterate over all auto match rivals
|
||||
await self.set_team_to_rival_data(resp.rival_data.auto_match)
|
||||
# iterate over all power match data
|
||||
await self.set_team_to_rival_data(resp.rival_data.power_match)
|
||||
|
||||
return resp
|
||||
|
||||
async def set_team_to_rival_data(self, rival_data: list[Season3PowerMatch]):
|
||||
for rival in rival_data:
|
||||
rival_id = rival.id
|
||||
|
||||
# do not set a team for a CPU rival
|
||||
if rival_id == -1:
|
||||
continue
|
||||
|
||||
# check if they part of a team
|
||||
team_dto = await self.team_service.get_team_info_dto(rival_id)
|
||||
if team_dto:
|
||||
rival.stickerid = team_dto.sticker_id
|
||||
rival.teamName = team_dto.name
|
||||
|
||||
# get the league rank
|
||||
league_info_dto = await self.team_service.get_league_info_dto(rival_id)
|
||||
rival.league_rank_id = league_info_dto.league_rank_id
|
||||
@@ -0,0 +1,21 @@
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.controller.season2.time_trial_controller import (
|
||||
IDACSeason2TimeTrialController,
|
||||
)
|
||||
from titles.idac.gameconfig.season3_config import IDACSeason3Config
|
||||
from titles.idac.service.season3.time_trial_service import IDACSeason3TimeTrialService
|
||||
|
||||
|
||||
class IDACSeason3TimeTrialController(IDACSeason2TimeTrialController):
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason3Config,
|
||||
time_trial_service: IDACSeason3TimeTrialService,
|
||||
) -> None:
|
||||
super().__init__(core_cfg, server_cfg, game_cfg, time_trial_service)
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
@@ -0,0 +1,296 @@
|
||||
from typing import Dict
|
||||
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.config import IDACServerConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.controller.season2.user_controller import IDACSeason2UserController
|
||||
from titles.idac.gameconfig.season3_config import IDACSeason3Config
|
||||
from titles.idac.model.season2.common_model import RequestSeason2UserHeaders
|
||||
from titles.idac.model.season3.common_model import RequestSeason3UserHeaders
|
||||
from titles.idac.model.season3.user_model import *
|
||||
from titles.idac.service.season2.factory_service import IDACSeason2FactoryService
|
||||
from titles.idac.service.season3.arcade_service import IDACSeason3ArcadeService
|
||||
from titles.idac.service.season3.team_service import IDACSeason3TeamService
|
||||
from titles.idac.service.season3.time_trial_service import IDACSeason3TimeTrialService
|
||||
from titles.idac.service.season3.user_service import IDACSeason3UserService
|
||||
|
||||
|
||||
class IDACSeason3UserController(IDACSeason2UserController):
|
||||
RequestUserCreateAccount = RequestSeason3UserCreateAccount
|
||||
ResponseUserCreateAccount = ResponseSeason3UserCreateAccount
|
||||
|
||||
RequestUserGetData = RequestSeason3UserGetData
|
||||
ResponseUserGetData = ResponseSeason3UserGetData
|
||||
|
||||
RequestUserUpdateLogin = RequestSeason3UserUpdateLogin
|
||||
ResponseUserUpdateLogin = ResponseSeason3UserUpdateLogin
|
||||
|
||||
RequestUserUpdateTimeTrialResult = RequestSeason3UserUpdateTimeTrialResult
|
||||
ResponseUserUpdateTimeTrialResult = ResponseSeason3UserUpdateTimeTrialResult
|
||||
|
||||
RequestUserUpdateStoryResult = RequestSeason3UserUpdateStoryResult
|
||||
ResponseUserUpdateStoryResult = ResponseSeason3UserUpdateStoryResult
|
||||
|
||||
RequestUserUpdateSpecialResult = RequestSeason3UserUpdateSpecialResult
|
||||
ResponseUserUpdateSpecialResult = ResponseSeason3UserUpdateSpecialResult
|
||||
|
||||
RequestUserUpdateTheoryResult = RequestSeason3UserUpdateTheoryResult
|
||||
ResponseUserUpdateTheoryResult = ResponseSeason3UserUpdateTheoryResult
|
||||
|
||||
RequestUserUpdateChallengeModeResult = RequestSeason3UserUpdateChallengeModeResult
|
||||
ResponseUserUpdateChallengeModeResult = ResponseSeason3UserUpdateChallengeModeResult
|
||||
|
||||
RequestUserUpdateStoreBattleResult = RequestSeason3UserUpdateStoreBattleResult
|
||||
ResponseUserUpdateStoreBattleResult = ResponseSeason3UserUpdateStoreBattleResult
|
||||
|
||||
RequestUserUpdateOnlineBattleResult = RequestSeason3UserUpdateOnlineBattleResult
|
||||
ResponseUserUpdateOnlineBattleResult = ResponseSeason3UserUpdateOnlineBattleResult
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
core_cfg: CoreConfig,
|
||||
server_cfg: IDACServerConfig,
|
||||
game_cfg: IDACSeason3Config,
|
||||
arcade_service: IDACSeason3ArcadeService,
|
||||
user_service: IDACSeason3UserService,
|
||||
time_trial_service: IDACSeason3TimeTrialService,
|
||||
factory_service: IDACSeason2FactoryService,
|
||||
team_service: IDACSeason3TeamService,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
core_cfg,
|
||||
server_cfg,
|
||||
game_cfg,
|
||||
arcade_service,
|
||||
user_service,
|
||||
time_trial_service,
|
||||
factory_service,
|
||||
)
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
|
||||
self.team_service = team_service
|
||||
|
||||
def _special_story_type(self, headers: RequestSeason2UserHeaders) -> StoryTypeEnum:
|
||||
ver = self._headers_to_version(headers)
|
||||
|
||||
return {210: StoryTypeEnum.HOT_VERSION}.get(ver, StoryTypeEnum.NONE)
|
||||
|
||||
async def handle_get_data_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseUserGetData:
|
||||
req = self.RequestUserGetData.model_validate(data)
|
||||
req_headers = RequestSeason3UserHeaders.model_validate(headers)
|
||||
|
||||
resp: ResponseSeason3UserGetData = await super().handle_get_data_request(
|
||||
data, headers
|
||||
)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
team_info = await self.team_service.get_team_info(user_id)
|
||||
league_info = None
|
||||
last_league_info = []
|
||||
league_rewards = []
|
||||
|
||||
if team_info:
|
||||
league_info = await self.team_service.get_league_info(
|
||||
team_info.team_id, user_id
|
||||
)
|
||||
|
||||
# check if the user already claimed the last league rewards
|
||||
claimed_rewards = await self.team_service.claimed_league_rewards(user_id)
|
||||
|
||||
if not claimed_rewards:
|
||||
# get past league info
|
||||
last_league_info = await self.team_service.get_past_league_info(team_info.team_id, user_id)
|
||||
|
||||
# generate league rewards based on the last league
|
||||
league_rewards = self.team_service.generate_league_rewards(last_league_info)
|
||||
|
||||
resp.team_info = team_info
|
||||
resp.league_info = league_info
|
||||
resp.league_rewards = league_rewards
|
||||
if last_league_info:
|
||||
# needs to be alist
|
||||
resp.last_league_info = [last_league_info]
|
||||
|
||||
return resp
|
||||
|
||||
async def handle_update_login_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseUserUpdateLogin:
|
||||
req_headers = RequestSeason3UserHeaders.model_validate(headers)
|
||||
req = self.RequestUserUpdateLogin.model_validate(data)
|
||||
resp: ResponseSeason3UserUpdateLogin = (
|
||||
await super().handle_update_login_request(data, headers)
|
||||
)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
# mark league rewards as received
|
||||
if len(req.league_rewards_id_arr) > 1:
|
||||
await self.team_service.update_past_league_rewards(user_id)
|
||||
|
||||
return resp
|
||||
|
||||
async def handle_update_time_trial_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseUserUpdateTimeTrialResult:
|
||||
req = self.RequestUserUpdateTimeTrialResult.model_validate(data)
|
||||
req_headers = RequestSeason3UserHeaders.model_validate(headers)
|
||||
resp: ResponseSeason3UserUpdateTimeTrialResult = (
|
||||
await super().handle_update_time_trial_result_request(data, headers)
|
||||
)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
update_result = await self.team_service.update_league_info(
|
||||
user_id, Season3UpdateRequestTeam.model_validate(req)
|
||||
)
|
||||
|
||||
resp.league_info = update_result.league_info
|
||||
resp.promote_effect_param = update_result.promote_effect_param
|
||||
resp.team_daily_bonus = update_result.team_daily_bonus
|
||||
resp.team_weekly_bonus = update_result.team_weekly_bonus
|
||||
|
||||
return resp
|
||||
|
||||
async def handle_update_story_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseUserUpdateStoryResult:
|
||||
req_headers = RequestSeason3UserHeaders.model_validate(headers)
|
||||
req = self.RequestUserUpdateStoryResult.model_validate(data)
|
||||
|
||||
resp: ResponseSeason3UserUpdateStoryResult = (
|
||||
await super().handle_update_story_result_request(data, headers)
|
||||
)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
update_result = await self.team_service.update_league_info(
|
||||
user_id, Season3UpdateRequestTeam.model_validate(req)
|
||||
)
|
||||
|
||||
resp.league_info = update_result.league_info
|
||||
resp.promote_effect_param = update_result.promote_effect_param
|
||||
resp.team_daily_bonus = update_result.team_daily_bonus
|
||||
resp.team_weekly_bonus = update_result.team_weekly_bonus
|
||||
|
||||
return resp
|
||||
|
||||
async def handle_update_special_mode_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseUserUpdateSpecialResult:
|
||||
req_headers = RequestSeason3UserHeaders.model_validate(headers)
|
||||
req = self.RequestUserUpdateSpecialResult.model_validate(data)
|
||||
|
||||
resp: ResponseSeason3UserUpdateSpecialResult = (
|
||||
await super().handle_update_special_mode_result_request(data, headers)
|
||||
)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
# rental flag is actually set in season 2 to not duplicate code here
|
||||
update_result = await self.team_service.update_league_info(
|
||||
user_id, Season3UpdateRequestTeam.model_validate(req)
|
||||
)
|
||||
|
||||
resp.league_info = update_result.league_info
|
||||
resp.promote_effect_param = update_result.promote_effect_param
|
||||
resp.team_daily_bonus = update_result.team_daily_bonus
|
||||
resp.team_weekly_bonus = update_result.team_weekly_bonus
|
||||
|
||||
return resp
|
||||
|
||||
async def handle_update_theory_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseUserUpdateTheoryResult:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestUserUpdateTheoryResult.model_validate(data)
|
||||
|
||||
resp: ResponseSeason3UserUpdateTheoryResult = (
|
||||
await super().handle_update_theory_result_request(data, headers)
|
||||
)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
update_result = await self.team_service.update_league_info(
|
||||
user_id, Season3UpdateRequestTeam.model_validate(req)
|
||||
)
|
||||
|
||||
resp.league_info = update_result.league_info
|
||||
resp.promote_effect_param = update_result.promote_effect_param
|
||||
resp.team_daily_bonus = update_result.team_daily_bonus
|
||||
resp.team_weekly_bonus = update_result.team_weekly_bonus
|
||||
|
||||
return resp
|
||||
|
||||
async def handle_update_challenge_mode_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseUserUpdateChallengeModeResult:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestUserUpdateChallengeModeResult.model_validate(data)
|
||||
|
||||
resp: ResponseSeason3UserUpdateChallengeModeResult = (
|
||||
await super().handle_update_challenge_mode_result_request(data, headers)
|
||||
)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
update_result = await self.team_service.update_league_info(
|
||||
user_id, Season3UpdateRequestTeam.model_validate(req)
|
||||
)
|
||||
|
||||
resp.league_info = update_result.league_info
|
||||
resp.promote_effect_param = update_result.promote_effect_param
|
||||
resp.team_daily_bonus = update_result.team_daily_bonus
|
||||
resp.team_weekly_bonus = update_result.team_weekly_bonus
|
||||
|
||||
return resp
|
||||
|
||||
async def handle_update_store_battle_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseUserUpdateStoreBattleResult:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestUserUpdateStoreBattleResult.model_validate(data)
|
||||
|
||||
resp: ResponseSeason3UserUpdateStoreBattleResult = (
|
||||
await super().handle_update_store_battle_result_request(data, headers)
|
||||
)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
update_result = await self.team_service.update_league_info(
|
||||
user_id, Season3UpdateRequestTeam.model_validate(req)
|
||||
)
|
||||
|
||||
resp.league_info = update_result.league_info
|
||||
resp.promote_effect_param = update_result.promote_effect_param
|
||||
resp.team_daily_bonus = update_result.team_daily_bonus
|
||||
resp.team_weekly_bonus = update_result.team_weekly_bonus
|
||||
|
||||
return resp
|
||||
|
||||
async def handle_update_online_battle_result_request(
|
||||
self, data: Dict, headers: Dict
|
||||
) -> ResponseUserUpdateOnlineBattleResult:
|
||||
req_headers = RequestSeason2UserHeaders.model_validate(headers)
|
||||
req = self.RequestUserUpdateOnlineBattleResult.model_validate(data)
|
||||
|
||||
resp: ResponseSeason3UserUpdateOnlineBattleResult = (
|
||||
await super().handle_update_online_battle_result_request(data, headers)
|
||||
)
|
||||
|
||||
user_id = req_headers.session
|
||||
|
||||
update_result = await self.team_service.update_league_info(
|
||||
user_id, Season3UpdateRequestTeam.model_validate(req)
|
||||
)
|
||||
|
||||
resp.league_info = update_result.league_info
|
||||
resp.promote_effect_param = update_result.promote_effect_param
|
||||
resp.team_daily_bonus = update_result.team_daily_bonus
|
||||
resp.team_weekly_bonus = update_result.team_weekly_bonus
|
||||
|
||||
return resp
|
||||
+21255
-21255
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,632 @@
|
||||
[
|
||||
{
|
||||
"league_rank_id": 0,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 6000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 0,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 12000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 0,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 18000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 0,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 24000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 0,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 30000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 0,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 40000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 0,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 60000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 0,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 80000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 0,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 100000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 2,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 1,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 6000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 1,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 12000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 1,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 18000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 1,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 24000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 1,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 30000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 1,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 40000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 1,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 60000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 1,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 80000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 1,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 100000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 2,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 2,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 6000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 2,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 12000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 2,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 18000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 2,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 24000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 2,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 30000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 2,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 40000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 2,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 60000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 2,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 80000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 2,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 100000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 2,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 3,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 6000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 3,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 12000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 3,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 18000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 3,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 24000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 3,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 30000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 3,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 40000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 3,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 60000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 3,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 80000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 3,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 100000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 2,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 4,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 6000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 4,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 12000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 4,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 18000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 4,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 24000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 4,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 30000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 4,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 40000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 4,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 60000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 4,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 80000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 4,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 100000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 2,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 5,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 6000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 5,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 12000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 5,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 18000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 5,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 24000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 5,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 30000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 5,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 40000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 5,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 60000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 5,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 80000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 5,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 100000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 2,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 6,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 6000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 6,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 12000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 6,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 18000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 6,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 24000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 6,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 30000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 6,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 40000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 6,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 60000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 6,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 80000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 1,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
},
|
||||
{
|
||||
"league_rank_id": 6,
|
||||
"target_type": 0,
|
||||
"rank_start": 0,
|
||||
"rank_end": 0,
|
||||
"team_point": 100000,
|
||||
"reward_category": 34,
|
||||
"reward_type": 2,
|
||||
"reward_reason": "チームポイント報酬"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,258 @@
|
||||
{
|
||||
"round_event_id": 31,
|
||||
"round_event_nm": "シーズン3 OPENラウンド",
|
||||
"round_start_rank": 11,
|
||||
"random_hidden_flag": 0,
|
||||
"save_filename": "230417_round_01_S3_1st",
|
||||
"vscount": [
|
||||
{
|
||||
"reward_upper_limit": 10,
|
||||
"reward_lower_limit": 10,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 34
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 40,
|
||||
"reward_lower_limit": 40,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 35
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 60,
|
||||
"reward_lower_limit": 60,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 7,
|
||||
"reward_type": 9
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 120,
|
||||
"reward_lower_limit": 120,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 317
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 160,
|
||||
"reward_lower_limit": 160,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 370
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"rank": [
|
||||
{
|
||||
"reward_upper_limit": 1,
|
||||
"reward_lower_limit": 1,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4627
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 2,
|
||||
"reward_lower_limit": 10,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4628
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 11,
|
||||
"reward_lower_limit": 50,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4629
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 51,
|
||||
"reward_lower_limit": 100,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4630
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 101,
|
||||
"reward_lower_limit": 1000,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4631
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"point": [],
|
||||
"playable_course_list": [
|
||||
{
|
||||
"course_id": 8,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 8,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 10,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 10,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 12,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 12,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 14,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 14,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 16,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 16,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 18,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 18,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 20,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 20,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 22,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 22,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 36,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 36,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 38,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 38,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 40,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 40,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 42,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 42,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 44,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 44,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 46,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 46,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 56,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 56,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 58,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 58,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 60,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 60,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 62,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 62,
|
||||
"course_day": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
{
|
||||
"round_event_id": 32,
|
||||
"round_event_nm": "シーズン3 2ndラウンド",
|
||||
"round_start_rank": 11,
|
||||
"random_hidden_flag": 0,
|
||||
"save_filename": "230511_round_01_S3_2nd",
|
||||
"vscount": [
|
||||
{
|
||||
"reward_upper_limit": 10,
|
||||
"reward_lower_limit": 10,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 388
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 40,
|
||||
"reward_lower_limit": 40,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 520
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 60,
|
||||
"reward_lower_limit": 60,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 7,
|
||||
"reward_type": 11
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 120,
|
||||
"reward_lower_limit": 120,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 117
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 160,
|
||||
"reward_lower_limit": 160,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 145
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"rank": [
|
||||
{
|
||||
"reward_upper_limit": 1,
|
||||
"reward_lower_limit": 1,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4632
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 2,
|
||||
"reward_lower_limit": 10,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4633
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 11,
|
||||
"reward_lower_limit": 50,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4634
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 51,
|
||||
"reward_lower_limit": 100,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4635
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 101,
|
||||
"reward_lower_limit": 1000,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4636
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"point": [],
|
||||
"playable_course_list": [
|
||||
{
|
||||
"course_id": 4,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 4,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 6,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 6,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 12,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 12,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 14,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 14,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 16,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 16,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 18,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 18,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 28,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 28,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 30,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 30,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 56,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 56,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 58,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 58,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 60,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 60,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 62,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 62,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 68,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 68,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 70,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 70,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 72,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 72,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 74,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 74,
|
||||
"course_day": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+20
-8
@@ -1,16 +1,28 @@
|
||||
from core.data import Data
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.schema.profile import IDACProfileData
|
||||
from titles.idac.schema.item import IDACItemData
|
||||
from titles.idac.schema.rounds import IDACOnlineRounds
|
||||
from titles.idac.schema.factory import IDACFactoryData
|
||||
from titles.idac.schema.challenge_db import IDACChallengeDb
|
||||
from titles.idac.schema.profile_db import IDACProfileDb
|
||||
from titles.idac.schema.item_db import IDACItemDb
|
||||
from titles.idac.schema.round_db import IDACRoundDb
|
||||
from titles.idac.schema.factory_db import IDACFactoryDb
|
||||
from titles.idac.schema.story_db import IDACStoryDb
|
||||
from titles.idac.schema.team_db import IDACTeamDb
|
||||
from titles.idac.schema.theory_db import IDACTheoryDb
|
||||
from titles.idac.schema.time_trial_db import IDACTimeTrialDb
|
||||
from titles.idac.schema.vs_info_db import IDACVsInfoDb
|
||||
|
||||
|
||||
class IDACData(Data):
|
||||
def __init__(self, cfg: CoreConfig) -> None:
|
||||
super().__init__(cfg)
|
||||
|
||||
self.profile = IDACProfileData(cfg, self.session)
|
||||
self.item = IDACItemData(cfg, self.session)
|
||||
self.rounds = IDACOnlineRounds(cfg, self.session)
|
||||
self.factory = IDACFactoryData(cfg, self.session)
|
||||
self.profile = IDACProfileDb(cfg, self.session)
|
||||
self.story = IDACStoryDb(cfg, self.session)
|
||||
self.time_trial = IDACTimeTrialDb(cfg, self.session)
|
||||
self.vs_info = IDACVsInfoDb(cfg, self.session)
|
||||
self.theory = IDACTheoryDb(cfg, self.session)
|
||||
self.challenge = IDACChallengeDb(cfg, self.session)
|
||||
self.item = IDACItemDb(cfg, self.session)
|
||||
self.round = IDACRoundDb(cfg, self.session)
|
||||
self.factory = IDACFactoryDb(cfg, self.session)
|
||||
self.team = IDACTeamDb(cfg, self.session)
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ class IDACEchoUDP:
|
||||
self.transport = transport
|
||||
|
||||
def datagram_received(self, data, addr):
|
||||
logging.getLogger('idz').debug(f'Received echo from {addr}')
|
||||
logging.getLogger('idac').debug(f'Received echo from {addr}')
|
||||
self.transport.sendto(data, addr)
|
||||
|
||||
|
||||
|
||||
+348
-254
@@ -3,7 +3,10 @@ import yaml
|
||||
import jinja2
|
||||
|
||||
from os import path
|
||||
from typing import List, Any, Type
|
||||
from pydantic import BaseModel, Field, ValidationError, model_validator
|
||||
from typing import Dict, List, Any, Optional, Tuple
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.engine import Row
|
||||
from starlette.routing import Route, Mount
|
||||
from starlette.responses import Response, RedirectResponse, JSONResponse
|
||||
from starlette.requests import Request
|
||||
@@ -11,102 +14,98 @@ from starlette.requests import Request
|
||||
from core.frontend import FE_Base, UserSession
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.database import IDACData
|
||||
from titles.idac.schema.profile import *
|
||||
from titles.idac.schema.item import *
|
||||
from titles.idac.config import IDACConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.schema.model.challenge import ChallengeDb
|
||||
from titles.idac.schema.model.factory import CarDb
|
||||
from titles.idac.schema.model.item import (
|
||||
BattleGiftDb,
|
||||
StampDb,
|
||||
TicketDb,
|
||||
TimeTrialEventDb,
|
||||
)
|
||||
from titles.idac.schema.model.profile import (
|
||||
ProfileAvatarDb,
|
||||
ProfileConfigDb,
|
||||
ProfileDb,
|
||||
ProfileRankDb,
|
||||
ProfileStockDb,
|
||||
ProfileTheoryDb,
|
||||
ProfileTipsDb,
|
||||
)
|
||||
from titles.idac.schema.model.story import (
|
||||
StoryDb,
|
||||
StoryEpisodeDb,
|
||||
StoryEpisodeDifficultyDb,
|
||||
)
|
||||
from titles.idac.schema.model.theory import (
|
||||
TheoryCourseDb,
|
||||
TheoryPartnerDb,
|
||||
TheoryRunningDb,
|
||||
)
|
||||
from titles.idac.schema.model.time_trial import CourseDb, TrialDb
|
||||
from titles.idac.schema.model.vs_info import VsInfoDb
|
||||
from titles.idac.service.season3.time_trial_service import IDACSeason3TimeTrialService
|
||||
|
||||
|
||||
class RankingData:
|
||||
def __init__(
|
||||
self,
|
||||
rank: int,
|
||||
name: str,
|
||||
record: int,
|
||||
eval_id: int,
|
||||
store: str,
|
||||
style_car_id: int,
|
||||
update_date: str,
|
||||
) -> None:
|
||||
self.rank: int = rank
|
||||
self.name: str = name
|
||||
self.record: str = record
|
||||
self.store: str = store
|
||||
self.eval_id: int = eval_id
|
||||
self.style_car_id: int = style_car_id
|
||||
self.update_date: str = update_date
|
||||
|
||||
def make(self):
|
||||
return vars(self)
|
||||
class RankingData(BaseModel):
|
||||
rank: int
|
||||
name: str
|
||||
record: str
|
||||
eval_id: int
|
||||
store: str
|
||||
style_car_id: int
|
||||
update_date: str
|
||||
|
||||
|
||||
class RequestValidator:
|
||||
def __init__(self) -> None:
|
||||
self.success: bool = True
|
||||
self.error: str = ""
|
||||
|
||||
def validate_param(
|
||||
self,
|
||||
request_args: Dict[bytes, bytes],
|
||||
param_name: str,
|
||||
param_type: Type[None],
|
||||
default=None,
|
||||
required: bool = True,
|
||||
) -> None:
|
||||
# Check if the parameter is missing
|
||||
if param_name not in request_args:
|
||||
if required:
|
||||
self.success = False
|
||||
self.error += f"Missing parameter: '{param_name}'. "
|
||||
else:
|
||||
# If the parameter is not required,
|
||||
# return the default value if it exists
|
||||
return default
|
||||
return None
|
||||
|
||||
param_value = request_args[param_name]
|
||||
|
||||
# Check if the parameter type is not empty
|
||||
if param_type:
|
||||
try:
|
||||
# Attempt to convert the parameter value to the specified type
|
||||
param_value = param_type(param_value)
|
||||
except ValueError:
|
||||
# If the conversion fails, return an error
|
||||
self.success = False
|
||||
self.error += f"Invalid parameter type for '{param_name}'. "
|
||||
return None
|
||||
|
||||
return param_value
|
||||
class RankingRequest(BaseModel):
|
||||
course_id: int = Field(alias="courseId")
|
||||
page_number: int = Field(default=1, alias="pageNumber")
|
||||
version: int = Field(default=1, alias="season")
|
||||
|
||||
|
||||
class RankingRequest(RequestValidator):
|
||||
def __init__(self, request_args: Dict[bytes, bytes]) -> None:
|
||||
super().__init__()
|
||||
class RankingResponse(BaseModel):
|
||||
success: bool = False
|
||||
error: str = ""
|
||||
total_pages: int = 0
|
||||
total_records: int = 0
|
||||
updated_at: str = ""
|
||||
ranking: List[RankingData] = Field(default_factory=list)
|
||||
|
||||
self.course_id: int = self.validate_param(request_args, "courseId", int)
|
||||
self.page_number: int = self.validate_param(
|
||||
request_args, "pageNumber", int, default=1, required=False
|
||||
)
|
||||
@model_validator(mode="after")
|
||||
def set_default_error(self) -> "RankingResponse":
|
||||
# Emulates your original error fallback logic
|
||||
if not self.success and not self.error:
|
||||
self.error = "Unknown error."
|
||||
return self
|
||||
|
||||
|
||||
class RankingResponse:
|
||||
def __init__(self) -> None:
|
||||
self.success: bool = False
|
||||
self.error: str = ""
|
||||
self.total_pages: int = 0
|
||||
self.total_records: int = 0
|
||||
self.updated_at: str = ""
|
||||
self.ranking: list[RankingData] = []
|
||||
class ProfileRankingData(BaseModel):
|
||||
course: int
|
||||
rank: int
|
||||
record: str
|
||||
eval_id: int
|
||||
style_car_id: int
|
||||
update_date: str
|
||||
|
||||
def make(self):
|
||||
ret = vars(self)
|
||||
self.error = (
|
||||
"Unknown error." if not self.success and self.error == "" else self.error
|
||||
)
|
||||
ret["ranking"] = [rank.make() for rank in self.ranking]
|
||||
|
||||
return ret
|
||||
class ProfileRankingRequest(BaseModel):
|
||||
version: int = Field(default=1, alias="season")
|
||||
|
||||
|
||||
class ProfileRankingResponse(BaseModel):
|
||||
success: bool = False
|
||||
error: str = ""
|
||||
updated_at: str = ""
|
||||
ranking: List[ProfileRankingData] = Field(default_factory=list)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def set_default_error(self) -> "RankingResponse":
|
||||
# Emulates your original error fallback logic
|
||||
if not self.success and not self.error:
|
||||
self.error = "Unknown error."
|
||||
return self
|
||||
|
||||
|
||||
class IDACFrontend(FE_Base):
|
||||
isLeaf = False
|
||||
@@ -119,35 +118,43 @@ class IDACFrontend(FE_Base):
|
||||
self.data = IDACData(cfg)
|
||||
self.core_cfg = cfg
|
||||
self.game_cfg = IDACConfig()
|
||||
|
||||
if path.exists(f"{cfg_dir}/{IDACConstants.CONFIG_NAME}"):
|
||||
self.game_cfg.update(
|
||||
yaml.safe_load(open(f"{cfg_dir}/{IDACConstants.CONFIG_NAME}"))
|
||||
)
|
||||
|
||||
self.nav_name = "頭文字D THE ARCADE"
|
||||
# self.nav_name = "IDAC"
|
||||
|
||||
# TODO: Add version list
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||
|
||||
self.profile = IDACProfileFrontend(cfg, self.environment)
|
||||
self.ranking = IDACRankingFrontend(cfg, self.environment)
|
||||
|
||||
self.profile = IDACProfileFrontend(cfg, self.game_cfg, self.environment)
|
||||
self.ranking = IDACRankingFrontend(cfg, self.game_cfg, self.environment)
|
||||
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/", self.render_GET),
|
||||
Mount("/profile", routes=[
|
||||
Route("/", self.profile.render_GET),
|
||||
# dirty hack
|
||||
Route("/export.get", self.profile.render_GET),
|
||||
]),
|
||||
Mount("/ranking", routes=[
|
||||
Route("/", self.ranking.render_GET),
|
||||
# dirty hack
|
||||
Route("/const.get", self.ranking.render_GET),
|
||||
Route("/ranking.get", self.ranking.render_GET),
|
||||
]),
|
||||
Mount(
|
||||
"/profile",
|
||||
routes=[
|
||||
Route("/", self.profile.render_GET),
|
||||
Route("/ranking.get", self.profile.handle_get_profile_ranking),
|
||||
Route("/export.get", self.profile.handle_export_profile),
|
||||
],
|
||||
),
|
||||
Mount(
|
||||
"/ranking",
|
||||
routes=[
|
||||
Route("/", self.ranking.render_GET),
|
||||
Route("/const.get", self.ranking.handle_get_const),
|
||||
Route("/ranking.get", self.ranking.handle_get_ranking),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
async def render_GET(self, request: Request) -> bytes:
|
||||
async def render_GET(self, request: Request) -> Response:
|
||||
uri: str = request.url.path
|
||||
|
||||
template = self.environment.get_template(
|
||||
@@ -161,129 +168,153 @@ class IDACFrontend(FE_Base):
|
||||
if uri.startswith("/game/idac"):
|
||||
return RedirectResponse("/game/idac/ranking", 303)
|
||||
|
||||
return Response(template.render(
|
||||
title=f"{self.core_config.server.name} | {self.nav_name}",
|
||||
game_list=self.environment.globals["game_list"],
|
||||
sesh=vars(usr_sesh),
|
||||
active_page="idac",
|
||||
), media_type="text/html; charset=utf-8")
|
||||
return Response(
|
||||
template.render(
|
||||
title=f"{self.core_config.server.name} | {self.nav_name}",
|
||||
game_list=self.environment.globals["game_list"],
|
||||
sesh=vars(usr_sesh),
|
||||
active_page="idac",
|
||||
),
|
||||
media_type="text/html; charset=utf-8",
|
||||
)
|
||||
|
||||
async def render_POST(self, request: Request) -> bytes:
|
||||
pass
|
||||
|
||||
|
||||
class IDACRankingFrontend(FE_Base):
|
||||
def __init__(self, cfg: CoreConfig, environment: jinja2.Environment) -> None:
|
||||
def __init__(
|
||||
self, cfg: CoreConfig, game_cfg: IDACConfig, environment: jinja2.Environment
|
||||
) -> None:
|
||||
super().__init__(cfg, environment)
|
||||
self.data = IDACData(cfg)
|
||||
self.core_cfg = cfg
|
||||
self.game_cfg = game_cfg
|
||||
|
||||
self.nav_name = "頭文字D THE ARCADE"
|
||||
# TODO: Add version list
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||
|
||||
async def render_GET(self, request: Request) -> bytes:
|
||||
uri: str = request.url.path
|
||||
|
||||
self.const = {"car": [], "course": []}
|
||||
|
||||
with open("titles/idac/templates/const.json", "r", encoding="utf-8") as f:
|
||||
self.const = json.load(f)
|
||||
|
||||
self.available_seasons = [
|
||||
{"value": i, "name": season[season.find("Season") :]}
|
||||
for i, season in enumerate(IDACConstants.VERSION_STRING[1:], start=1)
|
||||
]
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
|
||||
async def render_GET(self, request: Request) -> Response:
|
||||
template = self.environment.get_template(
|
||||
"titles/idac/templates/ranking/index.jinja"
|
||||
)
|
||||
|
||||
usr_sesh = self.validate_session(request)
|
||||
if not usr_sesh:
|
||||
usr_sesh = UserSession()
|
||||
user_id = usr_sesh.user_id
|
||||
|
||||
# IDAC constants
|
||||
if uri.startswith("/game/idac/ranking/const.get"):
|
||||
# get the constants
|
||||
with open("titles/idac/templates/const.json", "r", encoding="utf-8") as f:
|
||||
constants = json.load(f)
|
||||
return Response(
|
||||
template.render(
|
||||
title=f"{self.core_config.server.name} | {self.nav_name}",
|
||||
game_list=self.environment.globals["game_list"],
|
||||
sesh=vars(usr_sesh),
|
||||
active_page="idac",
|
||||
active_tab="ranking",
|
||||
available_seasons=self.available_seasons,
|
||||
current_season=self.version,
|
||||
),
|
||||
media_type="text/html; charset=utf-8",
|
||||
)
|
||||
|
||||
return JSONResponse(constants)
|
||||
async def handle_get_const(self, request: Request) -> JSONResponse:
|
||||
return JSONResponse(self.const)
|
||||
|
||||
# leaderboard ranking
|
||||
elif uri.startswith("/game/idac/ranking/ranking.get"):
|
||||
req = RankingRequest(request.query_params._dict)
|
||||
resp = RankingResponse()
|
||||
async def handle_get_ranking(self, request: Request) -> JSONResponse:
|
||||
resp = RankingResponse()
|
||||
|
||||
if not req.success:
|
||||
resp.error = req.error
|
||||
return JSONResponse(resp.make())
|
||||
try:
|
||||
# Let Pydantic validate the Starlette query parameters automatically
|
||||
req = RankingRequest(**request.query_params)
|
||||
except ValidationError as e:
|
||||
# If required params are missing or wrong types, catch it here
|
||||
resp.error = "Invalid or missing parameters."
|
||||
return JSONResponse(resp.model_dump())
|
||||
|
||||
# get the total number of records
|
||||
total_records = await self.data.item.get_time_trial_ranking_by_course_total(
|
||||
self.version, req.course_id
|
||||
# get the total number of records
|
||||
total_records = (
|
||||
await self.data.time_trial.get_time_trial_ranking_by_course_total(
|
||||
req.version, req.course_id
|
||||
)
|
||||
# return an error if there are no records
|
||||
if total_records is None or total_records == 0:
|
||||
resp.error = "No records found."
|
||||
return JSONResponse(resp.make())
|
||||
)
|
||||
|
||||
# get the total number of records
|
||||
total = total_records["count"]
|
||||
# return an error if there are no records
|
||||
if total_records == 0:
|
||||
resp.error = "No records found."
|
||||
return JSONResponse(resp.model_dump())
|
||||
|
||||
limit = 50
|
||||
offset = (req.page_number - 1) * limit
|
||||
total = total_records
|
||||
limit = 50
|
||||
offset = (req.page_number - 1) * limit
|
||||
|
||||
ranking = await self.data.item.get_time_trial_ranking_by_course(
|
||||
self.version,
|
||||
req.course_id,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
ranking = await self.data.time_trial.get_time_trial_ranking_by_course(
|
||||
req.version,
|
||||
req.course_id,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
|
||||
for i, rank in enumerate(ranking):
|
||||
user_id = rank["user"]
|
||||
for i, rank in enumerate(ranking):
|
||||
user_id = rank.user
|
||||
|
||||
# get the username, country and store from the profile
|
||||
profile = await self.data.profile.get_profile(user_id, self.version)
|
||||
arcade = await self.data.arcade.get_arcade(profile["store"])
|
||||
# get the username, country and store from the profile
|
||||
profile = await self.data.profile.get_profile(user_id, req.version)
|
||||
arcade = await self.data.arcade.get_arcade(profile.store)
|
||||
|
||||
if arcade is None:
|
||||
arcade = {}
|
||||
arcade["name"] = self.core_config.server.name
|
||||
if arcade is None:
|
||||
arcade = {"name": self.core_config.server.name}
|
||||
|
||||
# should never happen
|
||||
if profile is None:
|
||||
continue
|
||||
if profile is None:
|
||||
continue
|
||||
|
||||
resp.ranking.append(
|
||||
RankingData(
|
||||
rank=offset + i + 1,
|
||||
name=profile["username"],
|
||||
record=rank["goal_time"],
|
||||
store=arcade["name"],
|
||||
eval_id=rank["eval_id"],
|
||||
style_car_id=rank["style_car_id"],
|
||||
update_date=str(rank["play_dt"]),
|
||||
)
|
||||
resp.ranking.append(
|
||||
RankingData(
|
||||
rank=offset + i + 1,
|
||||
name=profile.username,
|
||||
record=str(rank.goal_time), # cast to str just to be safe
|
||||
store=arcade["name"],
|
||||
eval_id=rank.eval_id,
|
||||
style_car_id=rank.style_car_id,
|
||||
update_date=str(rank.play_dt),
|
||||
)
|
||||
)
|
||||
|
||||
# now return the json data, with the total number of pages and records
|
||||
# round up the total pages
|
||||
resp.success = True
|
||||
resp.total_pages = (total // limit) + 1
|
||||
resp.total_records = total
|
||||
return JSONResponse(resp.make())
|
||||
# finalize and serialize the response using model_dump()
|
||||
resp.success = True
|
||||
resp.total_pages = (total // limit) + 1
|
||||
resp.total_records = total
|
||||
|
||||
return Response(template.render(
|
||||
title=f"{self.core_config.server.name} | {self.nav_name}",
|
||||
game_list=self.environment.globals["game_list"],
|
||||
sesh=vars(usr_sesh),
|
||||
active_page="idac",
|
||||
active_tab="ranking",
|
||||
), media_type="text/html; charset=utf-8")
|
||||
return JSONResponse(resp.model_dump())
|
||||
|
||||
|
||||
class IDACProfileFrontend(FE_Base):
|
||||
def __init__(self, cfg: CoreConfig, environment: jinja2.Environment) -> None:
|
||||
def __init__(
|
||||
self, cfg: CoreConfig, game_cfg: IDACConfig, environment: jinja2.Environment
|
||||
) -> None:
|
||||
super().__init__(cfg, environment)
|
||||
self.data = IDACData(cfg)
|
||||
self.core_cfg = cfg
|
||||
self.game_cfg = game_cfg
|
||||
|
||||
self.nav_name = "頭文字D THE ARCADE"
|
||||
# TODO: Add version list
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_2
|
||||
|
||||
self.no_user_redirect = "/user/"
|
||||
|
||||
self.available_seasons = [
|
||||
{"value": i, "name": season[season.find("Season") :]}
|
||||
for i, season in enumerate(IDACConstants.VERSION_STRING[1:], start=1)
|
||||
]
|
||||
|
||||
self.version = IDACConstants.VER_IDAC_SEASON_3
|
||||
|
||||
self.ticket_names = {
|
||||
3: "car_dressup_points",
|
||||
@@ -292,87 +323,89 @@ class IDACProfileFrontend(FE_Base):
|
||||
34: "full_tune_fragments",
|
||||
}
|
||||
|
||||
self.time_trial_service = IDACSeason3TimeTrialService(
|
||||
self.core_cfg, self.game_cfg.server, self.game_cfg.season3
|
||||
)
|
||||
|
||||
async def get_validated_user(self, request: Request) -> Tuple[UserSession, Optional[Row]]:
|
||||
"""Helper to get session and user, returning None for user if not found."""
|
||||
usr_sesh = self.validate_session(request) or UserSession()
|
||||
user_id = usr_sesh.user_id
|
||||
|
||||
user = await self.data.user.get_user(user_id)
|
||||
if user is None:
|
||||
self.logger.debug(f"User {user_id} not found")
|
||||
return usr_sesh, None
|
||||
|
||||
return usr_sesh, user
|
||||
|
||||
async def generate_all_tables_json(self, user_id: int):
|
||||
json_export = {}
|
||||
|
||||
idac_tables = {
|
||||
profile,
|
||||
config,
|
||||
avatar,
|
||||
rank,
|
||||
stock,
|
||||
theory,
|
||||
car,
|
||||
ticket,
|
||||
story,
|
||||
episode,
|
||||
difficulty,
|
||||
course,
|
||||
trial,
|
||||
challenge,
|
||||
theory_course,
|
||||
theory_partner,
|
||||
theory_running,
|
||||
vs_info,
|
||||
stamp,
|
||||
timetrial_event,
|
||||
ProfileDb,
|
||||
ProfileConfigDb,
|
||||
ProfileAvatarDb,
|
||||
ProfileRankDb,
|
||||
ProfileStockDb,
|
||||
ProfileTheoryDb,
|
||||
ProfileTipsDb,
|
||||
CarDb,
|
||||
TicketDb,
|
||||
StoryDb,
|
||||
StoryEpisodeDb,
|
||||
StoryEpisodeDifficultyDb,
|
||||
CourseDb,
|
||||
TrialDb,
|
||||
ChallengeDb,
|
||||
TheoryCourseDb,
|
||||
TheoryPartnerDb,
|
||||
TheoryRunningDb,
|
||||
VsInfoDb,
|
||||
StampDb,
|
||||
TimeTrialEventDb,
|
||||
BattleGiftDb,
|
||||
}
|
||||
|
||||
for table in idac_tables:
|
||||
sql = select(table).where(
|
||||
table.c.user == user_id,
|
||||
for model in idac_tables:
|
||||
sql = select(model).where(
|
||||
model.user == user_id,
|
||||
)
|
||||
|
||||
# check if the table has a version column
|
||||
if "version" in table.c:
|
||||
sql = sql.where(table.c.version == self.version)
|
||||
# check if the model has a 'version' attribute
|
||||
if hasattr(model, "version"):
|
||||
sql = sql.where(model.version == self.version)
|
||||
|
||||
# lol use the profile connection for items, dirty hack
|
||||
result = await self.data.profile.execute(sql)
|
||||
data_list = result.fetchall()
|
||||
|
||||
# add the list to the json export with the correct table name
|
||||
json_export[table.name] = []
|
||||
for data in data_list:
|
||||
tmp = data._asdict()
|
||||
tmp.pop("id")
|
||||
tmp.pop("user")
|
||||
json_export[table.name].append(tmp)
|
||||
data_list = result.scalars().all()
|
||||
|
||||
table_name = model.__tablename__
|
||||
json_export[table_name] = []
|
||||
|
||||
for obj in data_list:
|
||||
# obj.__dict__ contains SQLAlchemy's internal state (_sa_instance_state),
|
||||
# so we filter out any keys starting with "_sa_"
|
||||
tmp = {
|
||||
k: v for k, v in obj.__dict__.items() if not k.startswith("_sa_")
|
||||
}
|
||||
|
||||
tmp.pop("id", None)
|
||||
tmp.pop("user", None)
|
||||
|
||||
json_export[table_name].append(tmp)
|
||||
|
||||
return json.dumps(json_export, indent=4, default=str, ensure_ascii=False)
|
||||
|
||||
async def render_GET(self, request: Request) -> bytes:
|
||||
uri: str = request.url.path
|
||||
|
||||
async def render_GET(self, request: Request) -> Response:
|
||||
template = self.environment.get_template(
|
||||
"titles/idac/templates/profile/index.jinja"
|
||||
)
|
||||
usr_sesh = self.validate_session(request)
|
||||
if not usr_sesh:
|
||||
usr_sesh = UserSession()
|
||||
user_id = usr_sesh.user_id
|
||||
|
||||
user = await self.data.user.get_user(user_id)
|
||||
|
||||
usr_sesh, user = await self.get_validated_user(request)
|
||||
if user is None:
|
||||
self.logger.debug(f"User {user_id} not found")
|
||||
return RedirectResponse("/user/", 303)
|
||||
|
||||
# profile export
|
||||
if uri.startswith("/game/idac/profile/export.get"):
|
||||
if user_id == 0:
|
||||
return RedirectResponse("/game/idac", 303)
|
||||
|
||||
# set the file name, content type and size to download the json
|
||||
content = await self.generate_all_tables_json(user_id)
|
||||
|
||||
self.logger.info(f"User {user_id} exported their IDAC data")
|
||||
|
||||
return Response(
|
||||
content.encode("utf-8"),
|
||||
200,
|
||||
{'content-disposition': 'attachment; filename=idac_profile.json'},
|
||||
"application/octet-stream"
|
||||
)
|
||||
return RedirectResponse(self.no_user_redirect, 303)
|
||||
user_id = usr_sesh.user_id
|
||||
|
||||
profile_data, tickets, rank = None, None, None
|
||||
if user_id > 0:
|
||||
@@ -382,18 +415,79 @@ class IDACProfileFrontend(FE_Base):
|
||||
|
||||
if ticket_data:
|
||||
tickets = {
|
||||
self.ticket_names[ticket["ticket_id"]]: ticket["ticket_cnt"]
|
||||
self.ticket_names[ticket.ticket_id]: ticket.ticket_cnt
|
||||
for ticket in ticket_data
|
||||
}
|
||||
|
||||
return Response(template.render(
|
||||
title=f"{self.core_config.server.name} | {self.nav_name}",
|
||||
game_list=self.environment.globals["game_list"],
|
||||
profile=profile_data,
|
||||
tickets=tickets,
|
||||
rank=rank,
|
||||
sesh=vars(usr_sesh),
|
||||
username=user["username"],
|
||||
active_page="idac",
|
||||
active_tab="profile",
|
||||
), media_type="text/html; charset=utf-8")
|
||||
return Response(
|
||||
template.render(
|
||||
title=f"{self.core_config.server.name} | {self.nav_name}",
|
||||
game_list=self.environment.globals["game_list"],
|
||||
profile=profile_data,
|
||||
tickets=tickets,
|
||||
rank=rank,
|
||||
sesh=vars(usr_sesh),
|
||||
username=user["username"],
|
||||
available_seasons=self.available_seasons,
|
||||
current_season=self.version,
|
||||
active_page="idac",
|
||||
active_tab="profile",
|
||||
),
|
||||
media_type="text/html; charset=utf-8",
|
||||
)
|
||||
|
||||
async def handle_get_profile_ranking(
|
||||
self, request: Request
|
||||
) -> JSONResponse:
|
||||
resp = ProfileRankingResponse()
|
||||
|
||||
usr_sesh, user = await self.get_validated_user(request)
|
||||
if user is None:
|
||||
return RedirectResponse(self.no_user_redirect, 303)
|
||||
user_id = usr_sesh.user_id
|
||||
|
||||
try:
|
||||
# Let Pydantic validate the Starlette query parameters automatically
|
||||
req = ProfileRankingRequest(**request.query_params)
|
||||
except ValidationError as e:
|
||||
# If required params are missing or wrong types, catch it here
|
||||
resp.error = "Invalid or missing parameters."
|
||||
return JSONResponse(resp.model_dump())
|
||||
|
||||
season_time_trial_data = (
|
||||
await self.time_trial_service.generate_season_time_trial_data(
|
||||
user_id, req.version
|
||||
)
|
||||
)
|
||||
|
||||
resp.success = True
|
||||
resp.ranking = [
|
||||
ProfileRankingData(
|
||||
course=time_trial.course_id,
|
||||
rank=time_trial.rank,
|
||||
record=str(time_trial.goal_time),
|
||||
eval_id=time_trial.eval_id,
|
||||
style_car_id=time_trial.style_car_id,
|
||||
update_date=str(time_trial.rank_dt),
|
||||
)
|
||||
for time_trial in season_time_trial_data
|
||||
]
|
||||
return JSONResponse(resp.model_dump())
|
||||
|
||||
async def handle_export_profile(self, request: Request) -> Response:
|
||||
usr_sesh, user = await self.get_validated_user(request)
|
||||
if user is None:
|
||||
return RedirectResponse(self.no_user_redirect, 303)
|
||||
user_id = usr_sesh.user_id
|
||||
|
||||
# set the file name, content type and size to download the json
|
||||
content = await self.generate_all_tables_json(user_id)
|
||||
|
||||
self.logger.info(f"User {user_id} exported their IDAC data")
|
||||
|
||||
return Response(
|
||||
content.encode("utf-8"),
|
||||
200,
|
||||
{"content-disposition": "attachment; filename=idac_profile.json"},
|
||||
"application/octet-stream",
|
||||
)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
from typing import Dict, List, Optional
|
||||
from datetime import datetime
|
||||
|
||||
from core.config import CoreConfig
|
||||
|
||||
|
||||
class IDACBaseFeature:
|
||||
"""Base class that binds a specific feature key and season."""
|
||||
|
||||
def __init__(self, config: dict, feature_key: str, season: str) -> None:
|
||||
self._config = config
|
||||
self._feature_key = feature_key
|
||||
self._season = season
|
||||
self._dt_format = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
def _get_val(self, *keys, default=None):
|
||||
return CoreConfig.get_config_field(
|
||||
self._config,
|
||||
"idac",
|
||||
self._feature_key,
|
||||
self._season,
|
||||
*keys,
|
||||
default=default
|
||||
)
|
||||
|
||||
def _get_dt(self, key: str, default_str: str) -> datetime:
|
||||
dt_str = self._get_val(key, default=default_str)
|
||||
try:
|
||||
return datetime.strptime(dt_str, self._dt_format)
|
||||
except (ValueError, TypeError):
|
||||
return datetime.strptime(default_str, self._dt_format)
|
||||
@@ -0,0 +1,276 @@
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from typing import Optional
|
||||
|
||||
from titles.idac.gameconfig.common_config import IDACBaseFeature
|
||||
from titles.idac.model.season2.config_model import (
|
||||
BattleGiftConfig,
|
||||
RoundConfig,
|
||||
StampInfoConfig,
|
||||
TimetrialConfig,
|
||||
)
|
||||
|
||||
SEASON = "S1"
|
||||
|
||||
|
||||
class IDACSeason1TimereleaseConfig(IDACBaseFeature):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, "timerelease", season)
|
||||
|
||||
@property
|
||||
def timerelease_no(self) -> int:
|
||||
return self._get_val("timerelease_no", default=1)
|
||||
|
||||
@property
|
||||
def timerelease_avatar_gacha_no(self) -> int:
|
||||
return self._get_val("timerelease_avatar_gacha_no", default=1)
|
||||
|
||||
|
||||
class IDACSeason1CollaboConfig(IDACBaseFeature):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, "collabo", season)
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return self._get_val("enable", default=False)
|
||||
|
||||
@property
|
||||
def collabo_id(self) -> int:
|
||||
return self._get_val("collabo_id", default=0)
|
||||
|
||||
|
||||
class IDACSeason1RoundConfig(IDACBaseFeature):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, "round", season)
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return self._get_val("enable", default=False)
|
||||
|
||||
@property
|
||||
def open_dt(self) -> datetime:
|
||||
return self._get_dt("open_dt", "2000-01-01 00:00:00")
|
||||
|
||||
@property
|
||||
def close_dt(self) -> datetime:
|
||||
return self._get_dt("close_dt", "2099-12-31 23:59:59")
|
||||
|
||||
@property
|
||||
def enabled_round(self) -> str:
|
||||
return self._get_val("enabled_round", default="")
|
||||
|
||||
@property
|
||||
def last_round(self) -> str:
|
||||
return self._get_val("last_round", default="")
|
||||
|
||||
|
||||
class IDACSeason1StampConfig(IDACBaseFeature):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, "stamp", season)
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return self._get_val("enable", default=False)
|
||||
|
||||
@property
|
||||
def open_dt(self) -> datetime:
|
||||
return self._get_dt("open_dt", "2000-01-01 00:00:00")
|
||||
|
||||
@property
|
||||
def close_dt(self) -> datetime:
|
||||
return self._get_dt("close_dt", "2099-12-31 23:59:59")
|
||||
|
||||
@property
|
||||
def enabled_stamps(self) -> dict:
|
||||
return self._get_val("enabled_stamps", default={})
|
||||
|
||||
|
||||
class IDACSeason1TimeTrialConfig(IDACBaseFeature):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, "timetrial", season)
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return self._get_val("enable", default=False)
|
||||
|
||||
@property
|
||||
def open_dt(self) -> datetime:
|
||||
return self._get_dt("open_dt", "2000-01-01 00:00:00")
|
||||
|
||||
@property
|
||||
def close_dt(self) -> datetime:
|
||||
return self._get_dt("close_dt", "2099-12-31 23:59:59")
|
||||
|
||||
@property
|
||||
def enabled_timetrials(self) -> Optional[str]:
|
||||
return self._get_val("enabled_timetrials", default={})
|
||||
|
||||
|
||||
class IDACSeason1BattleGiftConfig(IDACBaseFeature):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, "battle_gift", season)
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return self._get_val("enable", default=False)
|
||||
|
||||
@property
|
||||
def open_dt(self) -> datetime:
|
||||
return self._get_dt("open_dt", "2000-01-01 00:00:00")
|
||||
|
||||
@property
|
||||
def close_dt(self) -> datetime:
|
||||
return self._get_dt("close_dt", "2099-12-31 23:59:59")
|
||||
|
||||
@property
|
||||
def enabled_battle_gifts(self) -> dict:
|
||||
return self._get_val("enabled_battle_gifts", default={})
|
||||
|
||||
|
||||
class IDACSeason1Config(dict):
|
||||
def __init__(self, parent) -> None:
|
||||
self.logger = logging.getLogger("idac")
|
||||
|
||||
self.timerelease = IDACSeason1TimereleaseConfig(parent)
|
||||
self.collabo = IDACSeason1CollaboConfig(parent)
|
||||
self.stamp = IDACSeason1StampConfig(parent)
|
||||
self.timetrial = IDACSeason1TimeTrialConfig(parent)
|
||||
self.battle_gift = IDACSeason1BattleGiftConfig(parent)
|
||||
self.round = IDACSeason1RoundConfig(parent)
|
||||
|
||||
self.stamp_info: dict[int, list[StampInfoConfig]] = defaultdict(list)
|
||||
self.timetrial_info: dict[int, TimetrialConfig] = defaultdict()
|
||||
self.battle_gift_info: dict[int, BattleGiftConfig] = defaultdict()
|
||||
self.round_info: Optional[RoundConfig] = None
|
||||
self.last_round_info: Optional[RoundConfig] = None
|
||||
|
||||
def load_events(self):
|
||||
self.stamp_info = self.load_stamp_events()
|
||||
self.timetrial_info = self.load_time_trial_events()
|
||||
self.battle_gift_info = self.load_battle_gift_events()
|
||||
self.round_info = self.load_round_event()
|
||||
self.last_round_info = self.load_last_round_event()
|
||||
|
||||
def load_stamp_events(self) -> dict[int, list[StampInfoConfig]]:
|
||||
stamp_info: dict[int, list[StampInfoConfig]] = defaultdict(list)
|
||||
|
||||
if not self.stamp.enable:
|
||||
return stamp_info
|
||||
|
||||
for game_ver in self.stamp.enabled_stamps:
|
||||
for stamp in self.stamp.enabled_stamps[game_ver][:3]:
|
||||
file_path = f"./titles/idac/data/stamp/{stamp}.json"
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
self.logger.warning(f"Stamp {stamp} is enabled but does not exist!")
|
||||
continue
|
||||
|
||||
with open(file_path, encoding="UTF-8") as f:
|
||||
self.logger.debug(f"Loading stamp {stamp}")
|
||||
stamp_info_config = StampInfoConfig.model_validate(json.load(f))
|
||||
stamp_info_config.start_dt = self.stamp.open_dt
|
||||
stamp_info_config.end_dt = self.stamp.close_dt
|
||||
|
||||
stamp_info[game_ver].append(stamp_info_config)
|
||||
|
||||
return stamp_info
|
||||
|
||||
def load_time_trial_events(self) -> dict[int, TimetrialConfig]:
|
||||
timetrial_info: dict[int, TimetrialConfig] = defaultdict()
|
||||
|
||||
if not self.timetrial.enable:
|
||||
return timetrial_info
|
||||
|
||||
for game_ver in self.timetrial.enabled_timetrials.keys():
|
||||
timetrial = self.timetrial.enabled_timetrials[game_ver]
|
||||
file_path = f"./titles/idac/data/timetrial/{timetrial}.json"
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
self.logger.warning(
|
||||
f"Timetrial {timetrial} is enabled but does not exist!"
|
||||
)
|
||||
continue
|
||||
|
||||
with open(file_path, encoding="UTF-8") as f:
|
||||
self.logger.debug(f"Loading Timetrial {timetrial}")
|
||||
timetrial_config = TimetrialConfig.model_validate(json.load(f))
|
||||
timetrial_config.start_dt = self.timetrial.open_dt
|
||||
timetrial_config.end_dt = self.timetrial.close_dt
|
||||
|
||||
timetrial_info[game_ver] = timetrial_config
|
||||
|
||||
return timetrial_info
|
||||
|
||||
def load_battle_gift_events(self) -> dict[int, BattleGiftConfig]:
|
||||
battle_gift_info: dict[int, BattleGiftConfig] = defaultdict()
|
||||
|
||||
if not self.battle_gift.enable:
|
||||
return battle_gift_info
|
||||
|
||||
for game_ver in self.battle_gift.enabled_battle_gifts.keys():
|
||||
battle_gift = self.battle_gift.enabled_battle_gifts[game_ver]
|
||||
file_path = f"./titles/idac/data/battle_gift/{battle_gift}.json"
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
self.logger.warning(
|
||||
f"Battle Gift {battle_gift} is enabled but does not exist!"
|
||||
)
|
||||
continue
|
||||
|
||||
with open(file_path, encoding="UTF-8") as f:
|
||||
self.logger.debug(f"Loading Battle Gift {battle_gift}")
|
||||
battle_gift_config = BattleGiftConfig.model_validate(json.load(f))
|
||||
battle_gift_config.start_dt = self.battle_gift.open_dt
|
||||
battle_gift_config.end_dt = self.battle_gift.close_dt
|
||||
|
||||
battle_gift_info[game_ver] = battle_gift_config
|
||||
|
||||
return battle_gift_info
|
||||
|
||||
def load_round_event(self) -> Optional[RoundConfig]:
|
||||
round_info: Optional[RoundConfig] = None
|
||||
|
||||
if not self.round.enable:
|
||||
return round_info
|
||||
|
||||
round = self.round.enabled_round
|
||||
file_path = f"./titles/idac/data/round/{round}.json"
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
self.logger.warning(f"Round {round} is enabled but does not exist!")
|
||||
return round_info
|
||||
|
||||
with open(file_path, encoding="UTF-8") as f:
|
||||
self.logger.debug(f"Loading Round {round}")
|
||||
round_info = RoundConfig.model_validate(json.load(f))
|
||||
round_info.start_dt = self.round.open_dt
|
||||
round_info.end_dt = self.round.close_dt
|
||||
|
||||
return round_info
|
||||
|
||||
def load_last_round_event(self) -> Optional[RoundConfig]:
|
||||
round_info: Optional[RoundConfig] = None
|
||||
|
||||
if not self.round.enable:
|
||||
return round_info
|
||||
|
||||
round = self.round.last_round
|
||||
if not round:
|
||||
return
|
||||
|
||||
file_path = f"./titles/idac/data/round/{round}.json"
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
self.logger.warning(f"Last Round {round} is enabled but does not exist!")
|
||||
return round_info
|
||||
|
||||
with open(file_path, encoding="UTF-8") as f:
|
||||
self.logger.debug(f"Loading Last Round {round}")
|
||||
round_info = RoundConfig.model_validate(json.load(f))
|
||||
round_info.start_dt = self.round.open_dt - timedelta(weeks=4)
|
||||
round_info.end_dt = self.round.open_dt - timedelta(days=1)
|
||||
|
||||
return round_info
|
||||
@@ -0,0 +1,58 @@
|
||||
import logging
|
||||
|
||||
|
||||
from titles.idac.gameconfig.season1_config import (
|
||||
IDACSeason1BattleGiftConfig,
|
||||
IDACSeason1CollaboConfig,
|
||||
IDACSeason1Config,
|
||||
IDACSeason1RoundConfig,
|
||||
IDACSeason1StampConfig,
|
||||
IDACSeason1TimeTrialConfig,
|
||||
IDACSeason1TimereleaseConfig,
|
||||
)
|
||||
|
||||
SEASON = "S2"
|
||||
|
||||
class IDACSeason2TimereleaseConfig(IDACSeason1TimereleaseConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason2CollaboConfig(IDACSeason1CollaboConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason2StampConfig(IDACSeason1StampConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason2TimeTrialConfig(IDACSeason1TimeTrialConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason2BattleGiftConfig(IDACSeason1BattleGiftConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason2RoundConfig(IDACSeason1RoundConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason2Config(IDACSeason1Config):
|
||||
def __init__(self, parent) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self.logger = logging.getLogger("idac")
|
||||
self.season_name = SEASON
|
||||
|
||||
self.timerelease = IDACSeason2TimereleaseConfig(parent, self.season_name)
|
||||
self.collabo = IDACSeason2CollaboConfig(parent, self.season_name)
|
||||
self.stamp = IDACSeason2StampConfig(parent, self.season_name)
|
||||
self.timetrial = IDACSeason2TimeTrialConfig(parent, self.season_name)
|
||||
self.battle_gift = IDACSeason2BattleGiftConfig(parent, self.season_name)
|
||||
self.round = IDACSeason2RoundConfig(parent, self.season_name)
|
||||
@@ -0,0 +1,106 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from titles.idac.gameconfig.common_config import IDACBaseFeature
|
||||
from titles.idac.gameconfig.season2_config import (
|
||||
IDACSeason2BattleGiftConfig,
|
||||
IDACSeason2CollaboConfig,
|
||||
IDACSeason2Config,
|
||||
IDACSeason2RoundConfig,
|
||||
IDACSeason2StampConfig,
|
||||
IDACSeason2TimeTrialConfig,
|
||||
IDACSeason2TimereleaseConfig,
|
||||
)
|
||||
from titles.idac.model.season2.config_model import (
|
||||
LeagueRewardConfig,
|
||||
)
|
||||
|
||||
SEASON = "S3"
|
||||
|
||||
class IDACSeason3TimereleaseConfig(IDACSeason2TimereleaseConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason3CollaboConfig(IDACSeason2CollaboConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason3StampConfig(IDACSeason2StampConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason3TimeTrialConfig(IDACSeason2TimeTrialConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason3BattleGiftConfig(IDACSeason2BattleGiftConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason3RoundConfig(IDACSeason2RoundConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
@property
|
||||
def open_league(self) -> bool:
|
||||
return self._get_val("open_league", default=False)
|
||||
|
||||
|
||||
class IDACSeason3LeagueRewardConfig(IDACBaseFeature):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, "league_reward", season)
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return self._get_val("enable", default=False)
|
||||
|
||||
@property
|
||||
def enabled_league_reward(self) -> str:
|
||||
return self._get_val("enabled_league_reward", default="")
|
||||
|
||||
|
||||
class IDACSeason3Config(IDACSeason2Config):
|
||||
def __init__(self, parent) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self.timerelease = IDACSeason3TimereleaseConfig(parent)
|
||||
self.collabo = IDACSeason3CollaboConfig(parent)
|
||||
self.stamp = IDACSeason3StampConfig(parent)
|
||||
self.timetrial = IDACSeason3TimeTrialConfig(parent)
|
||||
self.battle_gift = IDACSeason3BattleGiftConfig(parent)
|
||||
self.round = IDACSeason3RoundConfig(parent)
|
||||
self.league_reward = IDACSeason3LeagueRewardConfig(parent)
|
||||
|
||||
self.league_reward_info: list[LeagueRewardConfig] = []
|
||||
|
||||
def load_events(self):
|
||||
super().load_events()
|
||||
|
||||
self.league_reward_info = self.load_league_reward_event()
|
||||
|
||||
def load_league_reward_event(self) -> list[LeagueRewardConfig]:
|
||||
league_reward_info: list[LeagueRewardConfig] = []
|
||||
|
||||
if not self.league_reward.enable:
|
||||
return league_reward_info
|
||||
|
||||
league_reward = self.league_reward.enabled_league_reward
|
||||
file_path = f"./titles/idac/data/league_reward/{league_reward}.json"
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
self.logger.warning(
|
||||
f"League Reward {league_reward} is enabled but does not exist!"
|
||||
)
|
||||
return league_reward_info
|
||||
|
||||
with open(file_path, encoding="UTF-8") as f:
|
||||
self.logger.debug(f"Loading League Reward {league_reward}")
|
||||
for league in json.load(f):
|
||||
league_reward_info.append(LeagueRewardConfig.model_validate(league))
|
||||
|
||||
return league_reward_info
|
||||
@@ -0,0 +1,61 @@
|
||||
from titles.idac.gameconfig.season3_config import (
|
||||
IDACSeason3BattleGiftConfig,
|
||||
IDACSeason3CollaboConfig,
|
||||
IDACSeason3Config,
|
||||
IDACSeason3LeagueRewardConfig,
|
||||
IDACSeason3RoundConfig,
|
||||
IDACSeason3StampConfig,
|
||||
IDACSeason3TimeTrialConfig,
|
||||
IDACSeason3TimereleaseConfig,
|
||||
)
|
||||
|
||||
|
||||
SEASON = "S4"
|
||||
|
||||
|
||||
class IDACSeason4TimereleaseConfig(IDACSeason3TimereleaseConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason4CollaboConfig(IDACSeason3CollaboConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason4StampConfig(IDACSeason3StampConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason4TimeTrialConfig(IDACSeason3TimeTrialConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason4BattleGiftConfig(IDACSeason3BattleGiftConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason4RoundConfig(IDACSeason3RoundConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason4LeagueRewardConfig(IDACSeason3LeagueRewardConfig):
|
||||
def __init__(self, parent: dict, season: str = SEASON) -> None:
|
||||
super().__init__(parent, season)
|
||||
|
||||
|
||||
class IDACSeason4Config(IDACSeason3Config):
|
||||
def __init__(self, parent) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self.timerelease = IDACSeason4TimereleaseConfig(parent)
|
||||
self.collabo = IDACSeason4CollaboConfig(parent)
|
||||
self.stamp = IDACSeason4StampConfig(parent)
|
||||
self.timetrial = IDACSeason4TimeTrialConfig(parent)
|
||||
self.battle_gift = IDACSeason4BattleGiftConfig(parent)
|
||||
self.round = IDACSeason4RoundConfig(parent)
|
||||
self.league_reward = IDACSeason4LeagueRewardConfig(parent)
|
||||
+79
-32
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
import traceback
|
||||
import inflection
|
||||
import yaml
|
||||
import logging
|
||||
import coloredlogs
|
||||
@@ -16,11 +17,14 @@ from core.config import CoreConfig
|
||||
from core.title import BaseServlet, JSONResponseNoASCII
|
||||
from core.utils import Utils
|
||||
from titles.idac.base import IDACBase
|
||||
from titles.idac.model.common_model import ResponseGeneric
|
||||
from titles.idac.season2 import IDACSeason2
|
||||
from titles.idac.season3 import IDACSeason3
|
||||
from titles.idac.config import IDACConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.echo import IDACEchoUDP
|
||||
|
||||
|
||||
class IDACServlet(BaseServlet):
|
||||
def __init__(self, core_cfg: CoreConfig, cfg_dir: str) -> None:
|
||||
self.core_cfg = core_cfg
|
||||
@@ -32,6 +36,8 @@ class IDACServlet(BaseServlet):
|
||||
|
||||
self.logger = logging.getLogger("idac")
|
||||
|
||||
self.queue = 0
|
||||
|
||||
if not hasattr(self.logger, "inited"):
|
||||
log_fmt_str = "[%(asctime)s] IDAC | %(levelname)s | %(message)s"
|
||||
log_fmt = logging.Formatter(log_fmt_str)
|
||||
@@ -55,14 +61,17 @@ class IDACServlet(BaseServlet):
|
||||
level=self.game_cfg.server.loglevel, logger=self.logger, fmt=log_fmt_str
|
||||
)
|
||||
self.logger.inited = True
|
||||
|
||||
self.versions = [
|
||||
|
||||
self.versions: list[IDACBase] = [
|
||||
IDACBase(core_cfg, self.game_cfg),
|
||||
IDACSeason2(core_cfg, self.game_cfg)
|
||||
IDACSeason2(core_cfg, self.game_cfg),
|
||||
IDACSeason3(core_cfg, self.game_cfg),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def is_game_enabled(cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str) -> bool:
|
||||
def is_game_enabled(
|
||||
cls, game_code: str, core_cfg: CoreConfig, cfg_dir: str
|
||||
) -> bool:
|
||||
game_cfg = IDACConfig()
|
||||
|
||||
if path.exists(f"{cfg_dir}/{IDACConstants.CONFIG_NAME}"):
|
||||
@@ -72,39 +81,55 @@ class IDACServlet(BaseServlet):
|
||||
|
||||
if not game_cfg.server.enable:
|
||||
return False
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_routes(self) -> List[Route]:
|
||||
return [
|
||||
Route("/{version:int}/initiald/{category:str}/{endpoint:str}", self.render_POST, methods=["POST"]),
|
||||
Route("/{version:int}/initiald-matching/{endpoint:str}", self.render_matching, methods=["POST"]),
|
||||
Route(
|
||||
"/{version:int}/initiald/{category:str}/{endpoint:str}",
|
||||
self.render_POST,
|
||||
methods=["POST"],
|
||||
),
|
||||
Route(
|
||||
"/{version:int}/initiald-matching/{endpoint:str}",
|
||||
self.render_matching,
|
||||
methods=["POST"],
|
||||
),
|
||||
]
|
||||
|
||||
def get_allnet_info(
|
||||
self, game_code: str, game_ver: int, keychip: str
|
||||
) -> Tuple[bool, str, str]:
|
||||
title_port_int = Utils.get_title_port(self.core_cfg)
|
||||
t_port = f":{title_port_int}" if title_port_int and not self.core_cfg.server.is_using_proxy else ""
|
||||
t_port = (
|
||||
f":{title_port_int}"
|
||||
if title_port_int and not self.core_cfg.server.is_using_proxy
|
||||
else ""
|
||||
)
|
||||
|
||||
return (
|
||||
f"",
|
||||
# requires http or else it defaults to https
|
||||
f"http://{self.core_cfg.server.hostname}{t_port}/{game_ver}/",
|
||||
# f"http://{self.core_cfg.server.hostname}{t_port}/{game_ver}/",
|
||||
f"http://sega-initiald.net/{game_ver}/",
|
||||
)
|
||||
|
||||
async def render_POST(self, request: Request) -> bytes:
|
||||
req_raw = await request.body()
|
||||
internal_ver = 0
|
||||
version: int = request.path_params.get('version')
|
||||
category: str = request.path_params.get('category')
|
||||
endpoint: str = request.path_params.get('endpoint')
|
||||
version: int = request.path_params.get("version")
|
||||
category: str = request.path_params.get("category").lower()
|
||||
endpoint: str = request.path_params.get("endpoint")
|
||||
client_ip = Utils.get_ip_addr(request)
|
||||
|
||||
internal_ver = IDACConstants.VER_IDAC_SEASON_1
|
||||
if version >= 100 and version < 140: # IDAC Season 1
|
||||
internal_ver = IDACConstants.VER_IDAC_SEASON_1
|
||||
elif version >= 140 and version < 171: # IDAC Season 2
|
||||
internal_ver = IDACConstants.VER_IDAC_SEASON_2
|
||||
elif version >= 200 and version < 240: # IDAC Season 3
|
||||
internal_ver = IDACConstants.VER_IDAC_SEASON_3
|
||||
|
||||
header_application = self.decode_header(request.headers.get("application", ""))
|
||||
|
||||
@@ -114,19 +139,33 @@ class IDACServlet(BaseServlet):
|
||||
self.logger.debug(f"Headers: {header_application}")
|
||||
self.logger.debug(req_data)
|
||||
|
||||
# func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
||||
func_to_find = "handle_"
|
||||
func_to_find += f"{category.lower()}_" if not category == "" else ""
|
||||
func_to_find += f"{endpoint.lower()}_request"
|
||||
|
||||
if not hasattr(self.versions[internal_ver], func_to_find):
|
||||
self.logger.warning(f"Unhandled v{version} request {endpoint}")
|
||||
return JSONResponse('{"status_code": "0"}')
|
||||
# if not hasattr(self.versions[internal_ver], func_to_find):
|
||||
# self.logger.warning(f"Unhandled v{version} request {func_to_find}")
|
||||
# return JSONResponse('{"status_code": "0"}')
|
||||
|
||||
resp = None
|
||||
try:
|
||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||
resp = await handler(req_data, header_application)
|
||||
if internal_ver == IDACConstants.VER_IDAC_SEASON_2:
|
||||
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
||||
controller: IDACSeason2 = self.versions[internal_ver].controllers[
|
||||
category
|
||||
]
|
||||
handler = getattr(controller, func_to_find)
|
||||
resp = await handler(req_data, header_application)
|
||||
elif internal_ver == IDACConstants.VER_IDAC_SEASON_3:
|
||||
func_to_find = "handle_" + inflection.underscore(endpoint) + "_request"
|
||||
controller: IDACSeason3 = self.versions[internal_ver].controllers[
|
||||
category
|
||||
]
|
||||
handler = getattr(controller, func_to_find)
|
||||
resp = await handler(req_data, header_application)
|
||||
else:
|
||||
handler = getattr(self.versions[internal_ver], func_to_find)
|
||||
resp = await handler(req_data, header_application)
|
||||
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
@@ -136,6 +175,9 @@ class IDACServlet(BaseServlet):
|
||||
if resp is None:
|
||||
resp = {"status_code": "0"}
|
||||
|
||||
if isinstance(resp, ResponseGeneric):
|
||||
resp = resp.model_dump_json_safe(exclude_none=True)
|
||||
|
||||
self.logger.debug(f"Response {resp}")
|
||||
return JSONResponseNoASCII(resp)
|
||||
|
||||
@@ -144,24 +186,20 @@ class IDACServlet(BaseServlet):
|
||||
ver: int = request.path_params.get("version")
|
||||
client_ip = Utils.get_ip_addr(request)
|
||||
req_data = await request.json()
|
||||
header_application = self.decode_header(request.headers.get('application', ''))
|
||||
header_application = self.decode_header(request.headers.get("application", ""))
|
||||
user_id = int(header_application["session"])
|
||||
|
||||
# self.getMatchingStatus(user_id)
|
||||
|
||||
self.logger.info(
|
||||
f"IDAC Matching request from {client_ip}: {url} - {req_data}"
|
||||
)
|
||||
self.logger.info(f"IDAC Matching request from {client_ip}: {url} - {req_data}")
|
||||
|
||||
resp = {"status_code": "0"}
|
||||
if url == "/regist":
|
||||
if url == "regist":
|
||||
self.queue = self.queue + 1
|
||||
elif url == "/status":
|
||||
elif url == "status":
|
||||
if req_data.get("cancel_flag"):
|
||||
self.queue = self.queue - 1
|
||||
self.logger.info(
|
||||
f"IDAC Matching endpoint {client_ip} had quited"
|
||||
)
|
||||
self.logger.info(f"IDAC Matching endpoint {client_ip} had quited")
|
||||
|
||||
resp = {
|
||||
"status_code": "0",
|
||||
@@ -190,9 +228,18 @@ class IDACServlet(BaseServlet):
|
||||
asyncio.create_task(
|
||||
loop.create_datagram_endpoint(
|
||||
lambda: IDACEchoUDP(),
|
||||
local_addr=(self.core_cfg.server.listen_address, self.game_cfg.server.echo1)
|
||||
local_addr=(
|
||||
self.core_cfg.server.listen_address,
|
||||
self.game_cfg.server.echo1,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
self.logger.info(f"Matching listening on {self.game_cfg.server.matching} with echo on {self.game_cfg.server.echo1}")
|
||||
|
||||
|
||||
self.logger.info(
|
||||
f"Matching listening on {self.game_cfg.server.matching} with echo on {self.game_cfg.server.echo1}"
|
||||
)
|
||||
|
||||
async def async_init(self):
|
||||
if self.game_cfg.server.enable:
|
||||
for season in self.versions:
|
||||
await season.async_init()
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
from titles.idac.model.season2.challenge_model import *
|
||||
from titles.idac.schema.model.challenge import *
|
||||
|
||||
|
||||
class IDACChallengeMapper:
|
||||
def challenge_to_dto(self, challenge: ChallengeDb) -> ChallengeDto:
|
||||
return ChallengeDto.model_validate(challenge)
|
||||
|
||||
def challenges_to_dto(self, challenges: list[ChallengeDb]) -> list[ChallengeDto]:
|
||||
return [self.challenge_to_dto(challenge) for challenge in challenges]
|
||||
|
||||
def row_to_best_challenge_dto(self, row) -> BestChallengeByVsTypeDto:
|
||||
return BestChallengeByVsTypeDto.model_validate(row)
|
||||
|
||||
def rows_to_best_challenge_dtos(self, rows: list) -> list[BestChallengeByVsTypeDto]:
|
||||
return [self.row_to_best_challenge_dto(row) for row in rows]
|
||||
@@ -0,0 +1,13 @@
|
||||
from titles.idac.model.season2.factory_model import CarDto, LotteryDto
|
||||
from titles.idac.schema.model.factory import CarDb, LotteryDb
|
||||
|
||||
|
||||
class IDACFactoryMapper:
|
||||
def lottery_to_dto(self, lottery: LotteryDb) -> LotteryDto:
|
||||
return LotteryDto.model_validate(lottery)
|
||||
|
||||
def car_to_dto(self, car: CarDb) -> CarDto:
|
||||
return CarDto.model_validate(car)
|
||||
|
||||
def cars_to_dto_list(self, cars_db: list[CarDb]) -> list[CarDto]:
|
||||
return [self.car_to_dto(car) for car in cars_db]
|
||||
@@ -0,0 +1,32 @@
|
||||
from typing import List
|
||||
|
||||
from titles.idac.model.season2.item_model import *
|
||||
from titles.idac.schema.model.item import *
|
||||
|
||||
|
||||
class IDACItemMapper:
|
||||
def ticket_to_dto(self, ticket: TicketDb) -> TicketDto:
|
||||
return TicketDto.model_validate(ticket)
|
||||
|
||||
def tickets_to_dto(self, tickets: List[TicketDb]) -> List[TicketDto]:
|
||||
return [self.ticket_to_dto(ticket) for ticket in tickets]
|
||||
|
||||
def stamp_to_dto(self, stamp: StampDb) -> StampDto:
|
||||
return StampDto.model_validate(stamp)
|
||||
|
||||
def stamps_to_dto(self, stamps: List[StampDb]) -> List[StampDto]:
|
||||
return [self.stamp_to_dto(stamp) for stamp in stamps]
|
||||
|
||||
def timetrial_event_to_dto(self, event: TimeTrialEventDb) -> TimeTrialEventDto:
|
||||
return TimeTrialEventDto.model_validate(event)
|
||||
|
||||
def timetrial_events_to_dto(
|
||||
self, events: list[TimeTrialEventDb]
|
||||
) -> list[TimeTrialEventDto]:
|
||||
return [self.timetrial_event_to_dto(event) for event in events]
|
||||
|
||||
def battle_gift_to_dto(self, gift: BattleGiftDb) -> BattleGiftDto:
|
||||
return BattleGiftDto.model_validate(gift)
|
||||
|
||||
def battle_gifts_to_dto(self, gifts: List[BattleGiftDb]) -> List[BattleGiftDto]:
|
||||
return [self.battle_gift_to_dto(gift) for gift in gifts]
|
||||
@@ -0,0 +1,32 @@
|
||||
from sqlalchemy.engine import Row
|
||||
|
||||
from titles.idac.model.season2.user_model import *
|
||||
from titles.idac.schema.model.profile import *
|
||||
|
||||
|
||||
class IDACProfileMapper:
|
||||
def profile_to_dto(self, profile: ProfileDb) -> ProfileDto:
|
||||
return ProfileDto.model_validate(profile)
|
||||
|
||||
def profile_config_to_dto(self, config: ProfileConfigDb) -> ProfileConfigDto:
|
||||
return ProfileConfigDto.model_validate(config)
|
||||
|
||||
def profile_avatar_to_dto(self, avatar: ProfileAvatarDb) -> ProfileAvatarDto:
|
||||
return ProfileAvatarDto.model_validate(avatar)
|
||||
|
||||
def profile_rank_to_dto(self, rank: ProfileRankDb) -> ProfileRankDto:
|
||||
return ProfileRankDto.model_validate(rank)
|
||||
|
||||
def profile_stock_to_dto(self, stock: ProfileStockDb) -> ProfileStockDto:
|
||||
return ProfileStockDto.model_validate(stock)
|
||||
|
||||
def profile_theory_to_dto(self, theory: ProfileTheoryDb) -> ProfileTheoryDto:
|
||||
return ProfileTheoryDto.model_validate(theory)
|
||||
|
||||
def profile_tips_to_dto(self, tips: ProfileTipsDb) -> ProfileTipsDto:
|
||||
return ProfileTipsDto.model_validate(tips)
|
||||
|
||||
def profile_config_dto_to_config_data(
|
||||
self, config: ProfileConfigDto
|
||||
) -> Season2ConfigData:
|
||||
return Season2ConfigData.model_validate(config)
|
||||
@@ -0,0 +1,36 @@
|
||||
from sqlalchemy.engine import Row
|
||||
|
||||
from titles.idac.model.season2.round_model import (
|
||||
RoundInfoDto,
|
||||
UserRoundInfoDto,
|
||||
UserRoundInfoWithRankDto,
|
||||
)
|
||||
from titles.idac.schema.model.round import RoundInfoDb, UserRoundInfoDb
|
||||
|
||||
|
||||
class IDACRoundMapper:
|
||||
def round_info_list_to_dto_list(
|
||||
self, round_info_list: list[RoundInfoDb]
|
||||
) -> list[RoundInfoDto]:
|
||||
return [self.round_info_to_dto(round) for round in round_info_list]
|
||||
|
||||
def round_info_to_dto(self, round_info: RoundInfoDb) -> RoundInfoDto:
|
||||
return RoundInfoDto.model_validate(round_info)
|
||||
|
||||
def user_round_info_to_dto(
|
||||
self, user_round_info: UserRoundInfoDb
|
||||
) -> UserRoundInfoDto:
|
||||
return UserRoundInfoDto.model_validate(user_round_info)
|
||||
|
||||
def user_round_info_with_rank_list_to_dto_list(
|
||||
self, user_round_info_list: list[Row]
|
||||
) -> list[UserRoundInfoWithRankDto]:
|
||||
return [
|
||||
self.user_round_info_with_rank_to_dto(user_round)
|
||||
for user_round in user_round_info_list
|
||||
]
|
||||
|
||||
def user_round_info_with_rank_to_dto(
|
||||
self, user_round_info: Row
|
||||
) -> UserRoundInfoWithRankDto:
|
||||
return UserRoundInfoWithRankDto.model_validate(user_round_info)
|
||||
@@ -0,0 +1,31 @@
|
||||
from titles.idac.model.season2.story_model import *
|
||||
from titles.idac.schema.model.story import *
|
||||
|
||||
|
||||
class IDACStoryMapper:
|
||||
def story_to_dto(self, story: StoryDb) -> StoryDto:
|
||||
return StoryDto.model_validate(story)
|
||||
|
||||
def stories_to_dto(self, stories: list[StoryDb]) -> list[StoryDto]:
|
||||
return [self.story_to_dto(story) for story in stories]
|
||||
|
||||
def story_episode_to_dto(self, episode: StoryEpisodeDb) -> StoryEpisodeDto:
|
||||
return StoryEpisodeDto.model_validate(episode)
|
||||
|
||||
def story_episodes_to_dto(
|
||||
self, episodes: list[StoryEpisodeDb]
|
||||
) -> list[StoryEpisodeDto]:
|
||||
return [self.story_episode_to_dto(episode) for episode in episodes]
|
||||
|
||||
def story_episode_difficulty_to_dto(
|
||||
self, difficulty: StoryEpisodeDifficultyDb
|
||||
) -> StoryEpisodeDifficultyDto:
|
||||
return StoryEpisodeDifficultyDto.model_validate(difficulty)
|
||||
|
||||
def story_episode_difficulties_to_dto(
|
||||
self, difficulties: list[StoryEpisodeDifficultyDb]
|
||||
) -> list[StoryEpisodeDifficultyDto]:
|
||||
return [
|
||||
self.story_episode_difficulty_to_dto(difficulty)
|
||||
for difficulty in difficulties
|
||||
]
|
||||
@@ -0,0 +1,98 @@
|
||||
from sqlalchemy.engine import Row
|
||||
|
||||
from titles.idac.model.season3.team_model import *
|
||||
from titles.idac.schema.model.team import *
|
||||
|
||||
|
||||
class IDACTeamMapper:
|
||||
def team_info_to_db(self, team_info: TeamInfoDto) -> TeamInfoDb:
|
||||
return TeamInfoDb(
|
||||
id=team_info.id,
|
||||
version=team_info.version,
|
||||
name=team_info.name,
|
||||
mileage=team_info.mileage,
|
||||
sticker_id=team_info.sticker_id,
|
||||
sticker_stock=team_info.sticker_stock,
|
||||
country=team_info.country,
|
||||
store_id=team_info.store_id,
|
||||
password=team_info.password,
|
||||
auto_invite_flag=team_info.auto_invite_flag,
|
||||
)
|
||||
|
||||
def team_info_list_to_dto_list(
|
||||
self, team_info_list: list[TeamInfoDb]
|
||||
) -> list[TeamInfoDto]:
|
||||
return [self.team_info_to_dto(team) for team in team_info_list]
|
||||
|
||||
def team_info_to_dto(self, team_info: TeamInfoDb) -> TeamInfoDto:
|
||||
return TeamInfoDto.model_validate(team_info)
|
||||
|
||||
def team_member_to_db(self, team_member: TeamMemberDto) -> TeamMemberDb:
|
||||
return TeamMemberDb(
|
||||
team_id=team_member.team_id,
|
||||
user=team_member.user,
|
||||
version=team_member.version,
|
||||
role=team_member.role,
|
||||
join_dt=team_member.join_dt,
|
||||
)
|
||||
|
||||
def team_member_to_dto(self, team_member: TeamMemberDb) -> TeamMemberDto:
|
||||
return TeamMemberDto.model_validate(team_member)
|
||||
|
||||
def team_member_with_ranking_to_dto(
|
||||
self, team_member: Row
|
||||
) -> TeamMemberWithRankingDto:
|
||||
return TeamMemberWithRankingDto.model_validate(team_member)
|
||||
|
||||
def all_team_member_with_ranking_to_dto(
|
||||
self, team_member: list[dict]
|
||||
) -> list[TeamMemberWithRankingDto]:
|
||||
return [self.team_member_with_ranking_to_dto(member) for member in team_member]
|
||||
|
||||
def all_team_member_to_dto(
|
||||
self, team_member: list[TeamMemberDb]
|
||||
) -> list[TeamMemberDto]:
|
||||
return [self.team_member_to_dto(member) for member in team_member]
|
||||
|
||||
def team_member_info_to_dto(
|
||||
self, team_member_info: TeamMemberInfoDb
|
||||
) -> TeamMemberInfoDto:
|
||||
return TeamMemberInfoDto.model_validate(team_member_info)
|
||||
|
||||
def all_team_member_info_to_dto(
|
||||
self, team_member_info: list[TeamMemberInfoDb]
|
||||
) -> list[TeamMemberInfoDto]:
|
||||
return [self.team_member_info_to_dto(member) for member in team_member_info]
|
||||
|
||||
def user_league_info_to_dto(
|
||||
self, user_league_info: UserLeagueInfoDb
|
||||
) -> UserLeagueInfoDto:
|
||||
return UserLeagueInfoDto.model_validate(user_league_info)
|
||||
|
||||
def league_info_list_to_dto_list(self, league_info_list: list[LeagueInfoDb]) -> list[LeagueInfoDto]:
|
||||
return [self.league_info_to_dto(league) for league in league_info_list]
|
||||
|
||||
def league_info_to_dto(self, league_info: LeagueInfoDb) -> LeagueInfoDto:
|
||||
return LeagueInfoDto.model_validate(league_info)
|
||||
|
||||
def league_info_with_ranking_to_dto(
|
||||
self, league_info: Row
|
||||
) -> LeagueInfoWithRankingDto:
|
||||
return LeagueInfoWithRankingDto.model_validate(league_info)
|
||||
|
||||
def random_team_info_to_dto(self, team_info: Row) -> RecommendTeamDto:
|
||||
team_info_db: TeamInfoDb = team_info["TeamInfoDb"]
|
||||
league_info_db: LeagueInfoDb = team_info["LeagueInfoDb"]
|
||||
return RecommendTeamDto(
|
||||
team_id=team_info_db.id,
|
||||
password=team_info_db.password,
|
||||
team_name=team_info_db.name,
|
||||
team_sticker_id=team_info_db.sticker_id,
|
||||
country=team_info_db.country,
|
||||
store_id=team_info_db.store_id,
|
||||
league_rank_id=league_info_db.league_rank_id,
|
||||
team_point=league_info_db.league_point,
|
||||
)
|
||||
|
||||
def random_team_infos_to_dto(self, team_infos: list[Row]) -> list[RecommendTeamDto]:
|
||||
return [self.random_team_info_to_dto(team_info) for team_info in team_infos]
|
||||
@@ -0,0 +1,32 @@
|
||||
from titles.idac.model.season2.theory_model import *
|
||||
from titles.idac.schema.model.profile import ProfileDb
|
||||
from titles.idac.schema.model.theory import *
|
||||
|
||||
|
||||
class IDACTheoryMapper:
|
||||
def theory_course_to_dto(self, course: TheoryCourseDb) -> TheoryCourseDto:
|
||||
return TheoryCourseDto.model_validate(course)
|
||||
|
||||
def theory_courses_to_dto(
|
||||
self, courses: list[TheoryCourseDb]
|
||||
) -> list[TheoryCourseDto]:
|
||||
return [self.theory_course_to_dto(course) for course in courses]
|
||||
|
||||
def theory_partner_to_dto(self, partner: TheoryPartnerDb) -> TheoryPartnerDto:
|
||||
return TheoryPartnerDto.model_validate(partner)
|
||||
|
||||
def theory_partners_to_dto(
|
||||
self, partners: list[TheoryPartnerDb]
|
||||
) -> list[TheoryPartnerDto]:
|
||||
return [self.theory_partner_to_dto(partner) for partner in partners]
|
||||
|
||||
def theory_running_to_dto(self, running: TheoryRunningDb) -> TheoryRunningDto:
|
||||
return TheoryRunningDto.model_validate(running)
|
||||
|
||||
def theory_runnings_to_dto(
|
||||
self, runnings: list[TheoryRunningDb]
|
||||
) -> list[TheoryRunningDto]:
|
||||
return [self.theory_running_to_dto(running) for running in runnings]
|
||||
|
||||
def theory_profile_to_dto(self, profile: ProfileDb) -> TheoryProfileDto:
|
||||
return TheoryProfileDto.model_validate(profile)
|
||||
@@ -0,0 +1,13 @@
|
||||
from titles.idac.model.season2.time_trial_model import CourseDto, TrialDto
|
||||
from titles.idac.schema.model.time_trial import CourseDb, TrialDb
|
||||
|
||||
|
||||
class IDACTimeTrialMapper:
|
||||
def course_to_dto(self, course: CourseDb) -> CourseDto:
|
||||
return CourseDto.model_validate(course)
|
||||
|
||||
def courses_to_dto_list(self, courses_db: list[CourseDb]) -> list[CourseDto]:
|
||||
return [self.course_to_dto(course) for course in courses_db]
|
||||
|
||||
def trial_to_dto(self, trial: TrialDb) -> TrialDto:
|
||||
return TrialDto.model_validate(trial)
|
||||
@@ -0,0 +1,18 @@
|
||||
from titles.idac.model.season2.vs_info_model import *
|
||||
from titles.idac.schema.model.vs_info import *
|
||||
|
||||
|
||||
class IDACVsInfoMapper:
|
||||
def vs_info_to_dto(self, vs_info: VsInfoDb) -> VsInfoDto:
|
||||
return VsInfoDto.model_validate(vs_info)
|
||||
|
||||
def vs_infos_to_dto(self, vs_infos: list[VsInfoDb]) -> list[VsInfoDto]:
|
||||
return [self.vs_info_to_dto(vs_info) for vs_info in vs_infos]
|
||||
|
||||
def vs_course_info_to_dto(self, course_info: VsCourseInfoDb) -> VsCourseInfoDto:
|
||||
return VsCourseInfoDto.model_validate(course_info)
|
||||
|
||||
def vs_course_infos_to_dto(
|
||||
self, course_infos: list[VsCourseInfoDb]
|
||||
) -> list[VsCourseInfoDto]:
|
||||
return [self.vs_course_info_to_dto(course_info) for course_info in course_infos]
|
||||
@@ -0,0 +1,433 @@
|
||||
from datetime import datetime
|
||||
from enum import Enum, IntEnum
|
||||
from typing import Optional
|
||||
from pydantic import AliasChoices, BaseModel, ConfigDict, Field, field_serializer
|
||||
|
||||
from titles.idac.const import (
|
||||
BattleGiftStatusEnum,
|
||||
CarMakerEnum,
|
||||
LayoutEnum,
|
||||
RewardCategoryEnum,
|
||||
StoryPlayStatusEnum,
|
||||
StoryTypeEnum,
|
||||
StyleEnum,
|
||||
)
|
||||
|
||||
|
||||
class DtoModel(BaseModel):
|
||||
model_config = ConfigDict(
|
||||
from_attributes=True,
|
||||
)
|
||||
|
||||
def model_dump_json_safe(self, **kwargs) -> dict:
|
||||
"""Dump model with datetime converted to timestamps for JSON serialization."""
|
||||
data = self.model_dump(**kwargs)
|
||||
return self._serialize_datetimes(data)
|
||||
|
||||
def model_dump_db(self, **kwargs) -> dict:
|
||||
"""
|
||||
Dump model with None values excluded and IntEnums converted to ints
|
||||
for SQLAlchemy.
|
||||
"""
|
||||
data = self.model_dump(exclude_none=True, **kwargs)
|
||||
return self._serialize_enums(data)
|
||||
|
||||
def _serialize_datetimes(self, obj):
|
||||
"""Recursively convert datetime objects to timestamps."""
|
||||
if isinstance(obj, datetime):
|
||||
return int(obj.timestamp())
|
||||
elif isinstance(obj, dict):
|
||||
return {key: self._serialize_datetimes(value) for key, value in obj.items()}
|
||||
elif isinstance(obj, list):
|
||||
return [self._serialize_datetimes(item) for item in obj]
|
||||
elif isinstance(obj, BaseModel):
|
||||
return self._serialize_datetimes(obj.model_dump())
|
||||
return obj
|
||||
|
||||
def _serialize_enums(self, obj):
|
||||
"""Recursively convert datetime objects to timestamps."""
|
||||
if isinstance(obj, IntEnum):
|
||||
return int(obj)
|
||||
if isinstance(obj, dict):
|
||||
return {k: self._serialize_enums(v) for k, v in obj.items()}
|
||||
if isinstance(obj, list):
|
||||
return [self._serialize_enums(v) for v in obj]
|
||||
return obj
|
||||
|
||||
|
||||
class Course(DtoModel):
|
||||
course_id: int
|
||||
course_day: int
|
||||
|
||||
|
||||
class Reward(DtoModel):
|
||||
reward_category: RewardCategoryEnum
|
||||
reward_type: int
|
||||
|
||||
|
||||
class Ticket(DtoModel):
|
||||
ticket_id: int
|
||||
ticket_cnt: int
|
||||
|
||||
|
||||
class CarUseCount(DtoModel):
|
||||
car_id: int
|
||||
count: int
|
||||
|
||||
|
||||
class MakerUseCount(DtoModel):
|
||||
maker_id: CarMakerEnum
|
||||
count: int
|
||||
|
||||
|
||||
class TimetrialEventData_2(DtoModel):
|
||||
timetrial_event_id: int
|
||||
point: int
|
||||
|
||||
|
||||
class Part(DtoModel):
|
||||
parts: int
|
||||
|
||||
|
||||
class LastUpdateDate(DtoModel):
|
||||
course_id: int
|
||||
# UNIX timestamp
|
||||
ranking_date: int
|
||||
|
||||
|
||||
class RewardDist(DtoModel):
|
||||
grade_reward_dist: int
|
||||
story_rank_reward_dist: int
|
||||
time_trial_rank_reward_dist: int
|
||||
online_battle_rank_reward_dist: int
|
||||
store_battle_rank_reward_dist: int
|
||||
theory_rank_reward_dist: int
|
||||
|
||||
|
||||
class DriverDebut(DtoModel):
|
||||
play_count: int
|
||||
daily_play: int
|
||||
last_play_dt: datetime
|
||||
use_start_date: datetime
|
||||
use_end_date: datetime
|
||||
ticket_cnt: int
|
||||
ticket_get_bit: int
|
||||
|
||||
|
||||
class AvatarData(DtoModel):
|
||||
sex: int
|
||||
face: int
|
||||
eye: int
|
||||
mouth: int
|
||||
hair: int
|
||||
glasses: int
|
||||
face_accessory: int
|
||||
body: int
|
||||
body_accessory: int
|
||||
behind: int
|
||||
bg: int
|
||||
effect: int
|
||||
special: int
|
||||
|
||||
|
||||
class StockData(DtoModel):
|
||||
mytitle_list: Optional[str] = None
|
||||
mytitle_new_list: Optional[str] = None
|
||||
avatar_face_list: Optional[str] = None
|
||||
avatar_face_new_list: Optional[str] = None
|
||||
avatar_eye_list: Optional[str] = None
|
||||
avatar_eye_new_list: Optional[str] = None
|
||||
avatar_hair_list: Optional[str] = None
|
||||
avatar_hair_new_list: Optional[str] = None
|
||||
avatar_body_list: Optional[str] = None
|
||||
avatar_body_new_list: Optional[str] = None
|
||||
avatar_mouth_list: Optional[str] = None
|
||||
avatar_mouth_new_list: Optional[str] = None
|
||||
avatar_glasses_list: Optional[str] = None
|
||||
avatar_glasses_new_list: Optional[str] = None
|
||||
avatar_face_accessory_list: Optional[str] = None
|
||||
avatar_face_accessory_new_list: Optional[str] = None
|
||||
avatar_body_accessory_list: Optional[str] = None
|
||||
avatar_body_accessory_new_list: Optional[str] = None
|
||||
avatar_behind_list: Optional[str] = None
|
||||
avatar_behind_new_list: Optional[str] = None
|
||||
avatar_bg_list: Optional[str] = None
|
||||
avatar_bg_new_list: Optional[str] = None
|
||||
avatar_effect_list: Optional[str] = None
|
||||
avatar_effect_new_list: Optional[str] = None
|
||||
avatar_special_list: Optional[str] = None
|
||||
avatar_special_new_list: Optional[str] = None
|
||||
stamp_list: Optional[str] = None
|
||||
stamp_new_list: Optional[str] = None
|
||||
keyholder_list: Optional[str] = None
|
||||
keyholder_new_list: Optional[str] = None
|
||||
papercup_list: Optional[str] = None
|
||||
papercup_new_list: Optional[str] = None
|
||||
tachometer_list: Optional[str] = None
|
||||
tachometer_new_list: Optional[str] = None
|
||||
aura_list: Optional[str] = None
|
||||
aura_new_list: Optional[str] = None
|
||||
aura_color_list: Optional[str] = None
|
||||
aura_color_new_list: Optional[str] = None
|
||||
aura_line_list: Optional[str] = None
|
||||
aura_line_new_list: Optional[str] = None
|
||||
bgm_list: Optional[str] = None
|
||||
bgm_new_list: Optional[str] = None
|
||||
dx_color_list: Optional[str] = None
|
||||
dx_color_new_list: Optional[str] = None
|
||||
start_menu_bg_list: Optional[str] = None
|
||||
start_menu_bg_new_list: Optional[str] = None
|
||||
under_neon_list: Optional[str] = None
|
||||
|
||||
|
||||
class ModeRank(DtoModel):
|
||||
story_rank: int
|
||||
story_rank_exp: int
|
||||
time_trial_rank: int
|
||||
time_trial_rank_exp: int
|
||||
online_battle_rank: int
|
||||
online_battle_rank_exp: int
|
||||
store_battle_rank: int
|
||||
store_battle_rank_exp: int
|
||||
theory_rank: int
|
||||
theory_exp: int
|
||||
pride_group_id: int
|
||||
pride_point: int
|
||||
is_last_max: int
|
||||
grade: int
|
||||
grade_exp: int
|
||||
|
||||
|
||||
class ModeRankData(DtoModel):
|
||||
story_rank_exp: int
|
||||
story_rank: int
|
||||
time_trial_rank_exp: int
|
||||
time_trial_rank: int
|
||||
online_battle_rank_exp: int
|
||||
online_battle_rank: int
|
||||
store_battle_rank_exp: int
|
||||
store_battle_rank: int
|
||||
theory_exp: int
|
||||
theory_rank: int
|
||||
pride_group_id: int
|
||||
pride_point: int
|
||||
grade_exp: int
|
||||
grade: int
|
||||
grade_reward_dist: int
|
||||
story_rank_reward_dist: int
|
||||
time_trial_rank_reward_dist: int
|
||||
online_battle_rank_reward_dist: int
|
||||
store_battle_rank_reward_dist: int
|
||||
theory_rank_reward_dist: int
|
||||
max_attained_online_battle_rank: int
|
||||
max_attained_pride_point: int
|
||||
is_last_max: int
|
||||
|
||||
|
||||
class CarData(DtoModel):
|
||||
id: int
|
||||
car_id: Optional[int] = None
|
||||
style_car_id: int
|
||||
color: int
|
||||
bureau: int
|
||||
kana: int
|
||||
s_no: int
|
||||
l_no: int
|
||||
car_flag: int
|
||||
tune_point: int
|
||||
tune_parts: datetime
|
||||
infinity_tune: int
|
||||
online_vs_win: int
|
||||
pickup_seq: int
|
||||
purchase_seq: int
|
||||
color_stock_list: str
|
||||
color_stock_new_list: str
|
||||
parts_stock_list: str
|
||||
parts_stock_new_list: str
|
||||
parts_set_equip_list: str
|
||||
use_count: int
|
||||
story_use_count: int
|
||||
timetrial_use_count: int
|
||||
vs_use_count: int
|
||||
net_vs_use_count: int
|
||||
theory_use_count: int
|
||||
car_mileage: int
|
||||
parts_list: list[Part]
|
||||
equip_parts_count: int
|
||||
|
||||
|
||||
class BestData(DtoModel):
|
||||
class RankingData(DtoModel):
|
||||
course_id: int
|
||||
rank: int
|
||||
username: str
|
||||
value: int
|
||||
store: str
|
||||
country: int
|
||||
style_car_id: int
|
||||
play_dt: datetime
|
||||
section_time_1: int
|
||||
section_time_2: int
|
||||
section_time_3: int
|
||||
section_time_4: int
|
||||
mission: int
|
||||
|
||||
course_id: int
|
||||
ranking_data: list[RankingData]
|
||||
|
||||
|
||||
class Story(DtoModel):
|
||||
class Episode(DtoModel):
|
||||
class Difficulty(DtoModel):
|
||||
difficulty: int
|
||||
play_count: int = 0
|
||||
clear_count: int = 0
|
||||
play_status: StoryPlayStatusEnum = StoryPlayStatusEnum.NEW
|
||||
play_score: int = 0
|
||||
|
||||
episode: int
|
||||
play_status: StoryPlayStatusEnum = StoryPlayStatusEnum.NEW
|
||||
difficulty_data: list[Difficulty]
|
||||
|
||||
story_type: StoryTypeEnum = StoryTypeEnum.STORY
|
||||
chapter: int
|
||||
loop_count: int = 1
|
||||
episode_data: list[Episode]
|
||||
|
||||
|
||||
class VsInfo(DtoModel):
|
||||
class VsinfoCourseData(DtoModel):
|
||||
course_id: int
|
||||
vs_cnt: int
|
||||
vs_win: int
|
||||
|
||||
battle_mode: int
|
||||
vs_cnt: int
|
||||
vs_win: int
|
||||
invalid: int
|
||||
str: int
|
||||
str_now: int
|
||||
lose_now: int
|
||||
vs_history: int
|
||||
course_select_priority: int
|
||||
break_count: int
|
||||
break_penalty_flag: bool
|
||||
vsinfo_course_data: list[VsinfoCourseData]
|
||||
|
||||
|
||||
class BattleGift(DtoModel):
|
||||
class Gift(DtoModel):
|
||||
gift_id: int
|
||||
gift_get_status: BattleGiftStatusEnum = BattleGiftStatusEnum.NEW
|
||||
|
||||
first_distribution_flag: int = 1
|
||||
gift_data: list[Gift]
|
||||
|
||||
|
||||
class RoundEvent(DtoModel):
|
||||
count: int
|
||||
win: int
|
||||
rank: int
|
||||
point: int
|
||||
total_round_point: int
|
||||
|
||||
|
||||
class PastRoundEvent(DtoModel):
|
||||
count: int
|
||||
win: int
|
||||
rank: int
|
||||
area_rank: int
|
||||
point: int
|
||||
total_round_point: int
|
||||
round_name: str
|
||||
round_id: int
|
||||
|
||||
|
||||
class StampEventData(DtoModel):
|
||||
m_stamp_event_id: int
|
||||
select_flag: int
|
||||
stamp_masu: int
|
||||
daily_bonus: int
|
||||
weekly_bonus: int
|
||||
weekday_bonus: int
|
||||
weekend_bonus: int
|
||||
total_bonus: int
|
||||
day_total_bonus: int
|
||||
store_battle_bonus: int
|
||||
story_bonus: int
|
||||
online_battle_bonus: int
|
||||
timetrial_bonus: int
|
||||
fasteststreetlegaltheory_bonus: int
|
||||
collaboration_bonus: int
|
||||
add_bonus_daily_flag_1: int
|
||||
add_bonus_daily_flag_2: int
|
||||
add_bonus_daily_flag_3: int
|
||||
new_flag: bool
|
||||
|
||||
|
||||
class SpecialMode(DtoModel):
|
||||
story_type: StoryTypeEnum
|
||||
vs_type: int
|
||||
max_clear_lv: int = Field(
|
||||
..., validation_alias=AliasChoices("max_clear_lv", "cleared_difficulty")
|
||||
)
|
||||
last_play_lv: int = Field(
|
||||
..., validation_alias=AliasChoices("last_play_lv", "play_difficulty")
|
||||
)
|
||||
last_play_course_id: int
|
||||
|
||||
|
||||
class ChallengeModeData(DtoModel):
|
||||
story_type: StoryTypeEnum = StoryTypeEnum.BUNTA
|
||||
vs_type: int
|
||||
max_clear_lv: int
|
||||
last_play_lv: int
|
||||
last_play_course_id: int
|
||||
play_count: int
|
||||
|
||||
|
||||
class TimetrialEventData2(DtoModel):
|
||||
timetrial_event_id: int
|
||||
point: int
|
||||
|
||||
|
||||
class CarLayoutCount(DtoModel):
|
||||
layout_id: LayoutEnum
|
||||
count: int
|
||||
|
||||
|
||||
class CarStyleCount(DtoModel):
|
||||
style_id: StyleEnum
|
||||
count: int
|
||||
|
||||
|
||||
class CarUseCount(DtoModel):
|
||||
car_id: int
|
||||
count: int
|
||||
|
||||
|
||||
class LockResultEnum(IntEnum):
|
||||
IN_USE = 0
|
||||
OK = 1
|
||||
NEWER_DATA = 2
|
||||
|
||||
|
||||
class StatusCodeEnum(str, Enum):
|
||||
OK = "0" # Success
|
||||
BAD_SESSION = "E1001" # Unknown requested session in headers
|
||||
REQUEST_FALSIFICATION = "E2002" # detect request Falsification
|
||||
UNKNOWN = "E3004" # UNKNOWN API
|
||||
BAD_HEADER = "E4001" # BAD HEADER
|
||||
BAD_REQUEST = "E4002" # BAD REQUEST FILED
|
||||
SERVER_ERROR = "E4003" # SERVER SIDE ERROR WHEN REQUESTING ID (OR TIMEOUT)
|
||||
OUT_OF_SERVICE = "E4004" # NOT YET SERVICING
|
||||
TIMEOUT = "E4005" # TIMEOUT/DEADLOCK
|
||||
SYSTEM_ERROR = "E9999" # SYSTEM ERROR
|
||||
|
||||
|
||||
class RequestGeneric(DtoModel):
|
||||
pass
|
||||
|
||||
|
||||
class ResponseGeneric(DtoModel):
|
||||
status_code: StatusCodeEnum = StatusCodeEnum.OK
|
||||
@@ -0,0 +1,18 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
from titles.idac.model.common_model import (
|
||||
BestData,
|
||||
LastUpdateDate,
|
||||
RequestGeneric,
|
||||
ResponseGeneric,
|
||||
)
|
||||
|
||||
|
||||
class RequestSeason2AdvertiseGetRankingData(RequestGeneric):
|
||||
last_update_date: list[LastUpdateDate]
|
||||
|
||||
|
||||
class ResponseSeason2AdvertiseGetRankingData(ResponseGeneric):
|
||||
national_best_data: list[BestData] = []
|
||||
shop_best_data: list[BestData] = []
|
||||
rank_management_flag: int = 0
|
||||
@@ -0,0 +1,10 @@
|
||||
from datetime import datetime
|
||||
from titles.idac.model.common_model import RequestGeneric, ResponseGeneric
|
||||
|
||||
RequestSeason2AliveGet = RequestGeneric
|
||||
|
||||
|
||||
class ResponseSeason2AliveGet(ResponseGeneric):
|
||||
# 1 Success, 0 = Failed
|
||||
server_status: int = 1
|
||||
force_reboot_time: datetime
|
||||
@@ -0,0 +1,618 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from titles.idac.const import CollaboEnum, StoryTypeEnum
|
||||
from titles.idac.model.common_model import *
|
||||
|
||||
|
||||
RequestSeason2BootGetGachaData = RequestGeneric
|
||||
|
||||
|
||||
class ResponseSeason2BootGetGachaData(ResponseGeneric):
|
||||
class AvatarGachaData(DtoModel):
|
||||
class GachaReward(DtoModel):
|
||||
reward_id: int
|
||||
reward_category: int
|
||||
reward_type: int
|
||||
rate: int
|
||||
pickup_flag: int
|
||||
|
||||
avatar_gacha_id: int
|
||||
avatar_gacha_nm: str
|
||||
gacha_type: int
|
||||
save_filename: str
|
||||
use_ticket_cnt: int
|
||||
start_dt: datetime
|
||||
end_dt: datetime
|
||||
gacha_reward: list[GachaReward]
|
||||
|
||||
avatar_gacha_data: list[AvatarGachaData]
|
||||
|
||||
|
||||
RequestSeason2BootGetTimeReleaseData = RequestGeneric
|
||||
|
||||
|
||||
class ResponseSeason2BootGetTimeReleaseData(ResponseGeneric):
|
||||
class Car(DtoModel):
|
||||
style_car_id: int
|
||||
release_status: int
|
||||
recommend: int
|
||||
new_status: int
|
||||
|
||||
class Chapter(DtoModel):
|
||||
timerelease_story: int
|
||||
timerelease_chapter: int
|
||||
|
||||
class AnnaunceImage(DtoModel):
|
||||
save_filename: str
|
||||
url: str
|
||||
# UNIX timestamp
|
||||
open_dt: int
|
||||
# UNIX timestamp
|
||||
close_dt: int
|
||||
display_id: int
|
||||
target_id: int
|
||||
page: int
|
||||
time: int
|
||||
|
||||
class VsBalanceCar(DtoModel):
|
||||
style_car_id: int
|
||||
course_id: int
|
||||
vs_balance_param1: int
|
||||
vs_balance_param2: int
|
||||
vs_balance_param3: int
|
||||
vs_balance_param4: int
|
||||
vs_balance_param5: int
|
||||
|
||||
class VsBalanceParam(DtoModel):
|
||||
course_id: int
|
||||
boost0: int
|
||||
boost1: int
|
||||
boost2: int
|
||||
boost3: int
|
||||
boost4: int
|
||||
boost5: int
|
||||
boost6: int
|
||||
|
||||
class MenuInfo(DtoModel):
|
||||
dressup_shop: int
|
||||
avatar_gacha: int
|
||||
car_dressup: int
|
||||
car_keyholder: int
|
||||
meter_customize: int
|
||||
aura_customize: int
|
||||
papercup_customize: int
|
||||
avatar_customize: int
|
||||
chat_stamp_customize: int
|
||||
mytitle_customize: int
|
||||
start_menu_customize: int
|
||||
name_change: int
|
||||
|
||||
class PartsList(DtoModel):
|
||||
car_id: int
|
||||
parts_id: int
|
||||
|
||||
display_timerelease_id: str
|
||||
car_list: list[Car]
|
||||
course_list: list[Course]
|
||||
chapter_list: list[Chapter]
|
||||
annaunce_image: list[AnnaunceImage]
|
||||
vs_balance_car: list[VsBalanceCar]
|
||||
vs_balance_param4: list[VsBalanceParam]
|
||||
vs_balance_param5: list[VsBalanceParam]
|
||||
vs_balance_param6: list[VsBalanceParam]
|
||||
vs_balance_param7: list[VsBalanceParam]
|
||||
menu_info: list[MenuInfo]
|
||||
parts_list: list[PartsList]
|
||||
|
||||
|
||||
class ResponseSeason2BootGetConfigData(ResponseGeneric):
|
||||
class BattleGiftEventMaster(DtoModel):
|
||||
class BattleGiftData(DtoModel):
|
||||
gift_id: int
|
||||
battle_gift_event_id: int
|
||||
reward_category: RewardCategoryEnum
|
||||
reward_type: int
|
||||
reward_name: str
|
||||
rarity: int
|
||||
cash_rate: int
|
||||
customize_point_rate: int
|
||||
avatar_point_rate: int
|
||||
first_distribution_rate: int
|
||||
|
||||
battle_gift_event_id: int
|
||||
event_nm: str
|
||||
# UNIX timestamp
|
||||
start_dt: datetime = datetime(1970, 1, 1)
|
||||
# UNIX timestamp
|
||||
end_dt: datetime = datetime(1970, 1, 1)
|
||||
mode_id: int
|
||||
delivery_type: int
|
||||
gift_data: list[BattleGiftData]
|
||||
|
||||
class RoundEvent_2(DtoModel):
|
||||
class VsCount(DtoModel):
|
||||
reward_upper_limit: int
|
||||
reward_lower_limit: int
|
||||
reward: list[Reward]
|
||||
|
||||
round_event_id: int
|
||||
round_event_nm: str
|
||||
# UNIX timestamp
|
||||
start_dt: datetime = datetime(1970, 1, 1)
|
||||
# UNIX timestamp
|
||||
end_dt: datetime = datetime(1970, 1, 1)
|
||||
round_start_rank: int = 11
|
||||
random_hidden_flag: int = 0
|
||||
save_filename: str
|
||||
vscount: list[VsCount] = []
|
||||
rank: list[VsCount] = []
|
||||
point: list[VsCount] = []
|
||||
playable_course_list: list[Course] = []
|
||||
|
||||
class LastRoundEventRanking(DtoModel):
|
||||
round_rank: int
|
||||
round_point: int
|
||||
round_play_count: int
|
||||
username: str
|
||||
country: int
|
||||
store: str
|
||||
online_battle_rank: int
|
||||
mytitle_id: int
|
||||
mytitle_effect_id: int
|
||||
car_data: list[CarData]
|
||||
user_avatar: AvatarData
|
||||
|
||||
class RoundEventExp(DtoModel):
|
||||
online_rank_id: int
|
||||
winbase: int
|
||||
losebase: int
|
||||
winnum: int
|
||||
|
||||
class StampInfo(DtoModel):
|
||||
class AddBonus(DtoModel):
|
||||
bonus_category: int
|
||||
bonus_play_num: int
|
||||
bonus_stamp_num: int
|
||||
bonus_daily_flag: int
|
||||
|
||||
class SheetSet(DtoModel):
|
||||
class StampSheet(DtoModel):
|
||||
reward_setting_masu: int
|
||||
reward_category_a: RewardCategoryEnum
|
||||
reward_type_a: int
|
||||
reward_category_b: RewardCategoryEnum
|
||||
reward_type_b: int
|
||||
reward_is_pickup: int
|
||||
reward_pickup_display_name: str
|
||||
|
||||
sheet_no: int
|
||||
loop_flag: int
|
||||
stamp_sheet: list[StampSheet]
|
||||
|
||||
m_stamp_event_id: int
|
||||
stamp_event_nm: str
|
||||
# UNIX timestamp
|
||||
start_dt: datetime = datetime(1970, 1, 1)
|
||||
# UNIX timestamp
|
||||
end_dt: datetime = datetime(1970, 1, 1)
|
||||
play_bonus: int
|
||||
daily_bonus: int
|
||||
weekly_bonus: int
|
||||
add_bonus: list[AddBonus]
|
||||
sheet_design: int
|
||||
sheet_stamp: int
|
||||
sheet_set: list[SheetSet]
|
||||
weekday_bonus: int
|
||||
weekend_bonus: int
|
||||
|
||||
class TakeoverReward(DtoModel):
|
||||
class TakeoverRewardData(DtoModel):
|
||||
reward_no: int
|
||||
reward_category: RewardCategoryEnum
|
||||
reward_type: int
|
||||
|
||||
takeover_reward_type: int
|
||||
takeover_reward_data: list[TakeoverRewardData]
|
||||
|
||||
class SubcardJudge(DtoModel):
|
||||
condition_id: int
|
||||
lower_rank: int
|
||||
higher_rank: int
|
||||
condition_start: int
|
||||
condition_end: int
|
||||
calc: int
|
||||
|
||||
class SpecialPromote(DtoModel):
|
||||
counter: int
|
||||
online_rank_id: int
|
||||
|
||||
class MatchingGroup(DtoModel):
|
||||
group_id: int
|
||||
group_percent: int
|
||||
|
||||
class SpecialModeData(DtoModel):
|
||||
start_dt: datetime
|
||||
end_dt: datetime
|
||||
story_type: StoryTypeEnum = StoryTypeEnum.NONE
|
||||
|
||||
class TimetrialEventData(DtoModel):
|
||||
class TTEventReward(DtoModel):
|
||||
point: int
|
||||
reward_category_a: RewardCategoryEnum
|
||||
reward_type_a: int
|
||||
reward_category_b: RewardCategoryEnum
|
||||
reward_type_b: int
|
||||
|
||||
timetrial_event_id: int
|
||||
name: str
|
||||
start_dt: datetime = datetime(1970, 1, 1)
|
||||
end_dt: datetime = datetime(1970, 1, 1)
|
||||
course_id: int
|
||||
point: list[int]
|
||||
reward: list[TTEventReward]
|
||||
|
||||
class NumberLotteryData(DtoModel):
|
||||
m_number_lottery_win_number_no: int
|
||||
m_number_lottery_schedule_no: int
|
||||
win_number: int
|
||||
reward_category: int
|
||||
reward_type: int
|
||||
end_dt: int
|
||||
|
||||
free_continue_enable: int = 1
|
||||
free_continue_new: int = 1
|
||||
free_continue_play: int = 1
|
||||
difference_time_to_jp: int = 0
|
||||
asset_version: str = "1"
|
||||
optional_version: str = "1"
|
||||
disconnect_offset: int = 0
|
||||
boost_balance_version: str = "0"
|
||||
time_release_number: str = "0"
|
||||
play_stamp_enable: int = 1
|
||||
play_stamp_bonus_coin: int = 1
|
||||
gacha_chara_needs: int = 1
|
||||
both_win_system_control: int = 0
|
||||
subcard_system_congrol: int = 0
|
||||
server_maintenance_start_hour: int = 0
|
||||
server_maintenance_start_minutes: int = 0
|
||||
server_maintenance_end_hour: int = 0
|
||||
server_maintenance_end_minutes: int = 0
|
||||
domain_api_game: str
|
||||
domain_matching: str
|
||||
domain_echo1: str
|
||||
domain_echo2: str
|
||||
domain_ping: str
|
||||
battle_gift_event_master: list[BattleGiftEventMaster] = []
|
||||
round_event: list[RoundEvent_2] = []
|
||||
last_round_event: list[RoundEvent_2] = []
|
||||
last_round_event_ranking: list[LastRoundEventRanking] = []
|
||||
round_event_exp: list[RoundEventExp] = [
|
||||
RoundEventExp(online_rank_id=0, winbase=0, losebase=0, winnum=0),
|
||||
RoundEventExp(online_rank_id=1, winbase=0, losebase=0, winnum=10),
|
||||
RoundEventExp(online_rank_id=2, winbase=0, losebase=0, winnum=10),
|
||||
RoundEventExp(online_rank_id=3, winbase=0, losebase=0, winnum=10),
|
||||
RoundEventExp(online_rank_id=4, winbase=0, losebase=0, winnum=10),
|
||||
RoundEventExp(online_rank_id=5, winbase=0, losebase=0, winnum=20),
|
||||
RoundEventExp(online_rank_id=6, winbase=0, losebase=0, winnum=20),
|
||||
RoundEventExp(online_rank_id=7, winbase=0, losebase=0, winnum=20),
|
||||
RoundEventExp(online_rank_id=8, winbase=0, losebase=0, winnum=30),
|
||||
RoundEventExp(online_rank_id=9, winbase=0, losebase=0, winnum=30),
|
||||
RoundEventExp(online_rank_id=10, winbase=0, losebase=0, winnum=30),
|
||||
RoundEventExp(online_rank_id=11, winbase=110, losebase=-60, winnum=50),
|
||||
RoundEventExp(online_rank_id=12, winbase=110, losebase=-60, winnum=50),
|
||||
RoundEventExp(online_rank_id=13, winbase=110, losebase=-60, winnum=50),
|
||||
RoundEventExp(online_rank_id=14, winbase=110, losebase=-60, winnum=50),
|
||||
RoundEventExp(online_rank_id=15, winbase=110, losebase=-60, winnum=50),
|
||||
RoundEventExp(online_rank_id=16, winbase=110, losebase=-60, winnum=70),
|
||||
RoundEventExp(online_rank_id=17, winbase=110, losebase=-60, winnum=70),
|
||||
RoundEventExp(online_rank_id=18, winbase=110, losebase=-60, winnum=70),
|
||||
RoundEventExp(online_rank_id=19, winbase=110, losebase=-60, winnum=70),
|
||||
RoundEventExp(online_rank_id=20, winbase=110, losebase=-60, winnum=70),
|
||||
RoundEventExp(online_rank_id=21, winbase=110, losebase=-60, winnum=90),
|
||||
RoundEventExp(online_rank_id=22, winbase=110, losebase=-60, winnum=90),
|
||||
RoundEventExp(online_rank_id=23, winbase=110, losebase=-60, winnum=90),
|
||||
RoundEventExp(online_rank_id=24, winbase=110, losebase=-60, winnum=90),
|
||||
RoundEventExp(online_rank_id=25, winbase=110, losebase=-60, winnum=90),
|
||||
RoundEventExp(online_rank_id=26, winbase=110, losebase=-60, winnum=110),
|
||||
RoundEventExp(online_rank_id=27, winbase=110, losebase=-60, winnum=110),
|
||||
RoundEventExp(online_rank_id=28, winbase=110, losebase=-60, winnum=110),
|
||||
RoundEventExp(online_rank_id=29, winbase=110, losebase=-60, winnum=110),
|
||||
RoundEventExp(online_rank_id=30, winbase=110, losebase=-60, winnum=110),
|
||||
RoundEventExp(online_rank_id=31, winbase=110, losebase=-60, winnum=110),
|
||||
RoundEventExp(online_rank_id=32, winbase=110, losebase=-60, winnum=130),
|
||||
RoundEventExp(online_rank_id=33, winbase=110, losebase=-60, winnum=130),
|
||||
RoundEventExp(online_rank_id=34, winbase=110, losebase=-60, winnum=130),
|
||||
RoundEventExp(online_rank_id=35, winbase=110, losebase=-60, winnum=130),
|
||||
RoundEventExp(online_rank_id=36, winbase=110, losebase=-60, winnum=130),
|
||||
RoundEventExp(online_rank_id=37, winbase=110, losebase=-60, winnum=130),
|
||||
RoundEventExp(online_rank_id=38, winbase=110, losebase=-60, winnum=130),
|
||||
RoundEventExp(online_rank_id=39, winbase=110, losebase=-60, winnum=150),
|
||||
RoundEventExp(online_rank_id=40, winbase=110, losebase=-60, winnum=150),
|
||||
RoundEventExp(online_rank_id=41, winbase=110, losebase=-60, winnum=150),
|
||||
RoundEventExp(online_rank_id=42, winbase=110, losebase=-60, winnum=150),
|
||||
RoundEventExp(online_rank_id=43, winbase=110, losebase=-60, winnum=150),
|
||||
RoundEventExp(online_rank_id=44, winbase=110, losebase=-60, winnum=150),
|
||||
RoundEventExp(online_rank_id=45, winbase=110, losebase=-60, winnum=150),
|
||||
RoundEventExp(online_rank_id=46, winbase=110, losebase=-60, winnum=150),
|
||||
RoundEventExp(online_rank_id=47, winbase=110, losebase=-60, winnum=170),
|
||||
RoundEventExp(online_rank_id=48, winbase=110, losebase=-60, winnum=170),
|
||||
RoundEventExp(online_rank_id=49, winbase=110, losebase=-60, winnum=170),
|
||||
RoundEventExp(online_rank_id=50, winbase=110, losebase=-60, winnum=170),
|
||||
RoundEventExp(online_rank_id=51, winbase=110, losebase=-60, winnum=170),
|
||||
RoundEventExp(online_rank_id=52, winbase=110, losebase=-60, winnum=170),
|
||||
RoundEventExp(online_rank_id=53, winbase=110, losebase=-60, winnum=170),
|
||||
RoundEventExp(online_rank_id=54, winbase=110, losebase=-60, winnum=170),
|
||||
RoundEventExp(online_rank_id=55, winbase=110, losebase=-60, winnum=170),
|
||||
RoundEventExp(online_rank_id=56, winbase=110, losebase=-60, winnum=190),
|
||||
RoundEventExp(online_rank_id=57, winbase=110, losebase=-60, winnum=190),
|
||||
RoundEventExp(online_rank_id=58, winbase=110, losebase=-60, winnum=190),
|
||||
RoundEventExp(online_rank_id=59, winbase=110, losebase=-60, winnum=190),
|
||||
RoundEventExp(online_rank_id=60, winbase=110, losebase=-60, winnum=190),
|
||||
RoundEventExp(online_rank_id=61, winbase=110, losebase=-60, winnum=190),
|
||||
RoundEventExp(online_rank_id=62, winbase=110, losebase=-60, winnum=190),
|
||||
RoundEventExp(online_rank_id=63, winbase=110, losebase=-60, winnum=190),
|
||||
]
|
||||
stamp_info: list[StampInfo] = []
|
||||
timerelease_no: int
|
||||
timerelease_avatar_gacha_no: int
|
||||
takeover_reward: list[TakeoverReward] = [
|
||||
TakeoverReward(
|
||||
takeover_reward_type=0,
|
||||
takeover_reward_data=[
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=1,
|
||||
reward_category=RewardCategoryEnum.D_COIN,
|
||||
reward_type=5000,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=2,
|
||||
reward_category=RewardCategoryEnum.FULLTUNE_TICKET,
|
||||
reward_type=1,
|
||||
),
|
||||
],
|
||||
),
|
||||
# ID Zero mileage rewards
|
||||
TakeoverReward(
|
||||
takeover_reward_type=1,
|
||||
takeover_reward_data=[
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=1,
|
||||
reward_category=RewardCategoryEnum.AVATAR_BODY,
|
||||
reward_type=82,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=1,
|
||||
reward_category=RewardCategoryEnum.AVATAR_BODY,
|
||||
reward_type=190,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=2,
|
||||
reward_category=RewardCategoryEnum.CAR_DRESSUP_TOKEN,
|
||||
reward_type=2,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=2,
|
||||
reward_category=RewardCategoryEnum.AVATAR_TOKEN,
|
||||
reward_type=2,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=3,
|
||||
reward_category=RewardCategoryEnum.CAR_DRESSUP_TOKEN,
|
||||
reward_type=3,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=3,
|
||||
reward_category=RewardCategoryEnum.AVATAR_TOKEN,
|
||||
reward_type=3,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=4,
|
||||
reward_category=RewardCategoryEnum.CAR_DRESSUP_TOKEN,
|
||||
reward_type=5,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=4,
|
||||
reward_category=RewardCategoryEnum.AVATAR_TOKEN,
|
||||
reward_type=5,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=5,
|
||||
reward_category=RewardCategoryEnum.AVATAR_BODY,
|
||||
reward_type=104,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=5,
|
||||
reward_category=RewardCategoryEnum.AVATAR_BODY,
|
||||
reward_type=212,
|
||||
),
|
||||
],
|
||||
),
|
||||
# ID Zero Charimsa level and fame benefits
|
||||
TakeoverReward(
|
||||
takeover_reward_type=2,
|
||||
takeover_reward_data=[
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=1,
|
||||
reward_category=RewardCategoryEnum.TITLE,
|
||||
reward_type=4052,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=2,
|
||||
reward_category=RewardCategoryEnum.TITLE,
|
||||
reward_type=4053,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=3,
|
||||
reward_category=RewardCategoryEnum.TITLE,
|
||||
reward_type=4054,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=4,
|
||||
reward_category=RewardCategoryEnum.TITLE,
|
||||
reward_type=4055,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=5,
|
||||
reward_category=RewardCategoryEnum.TITLE,
|
||||
reward_type=4056,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=6,
|
||||
reward_category=RewardCategoryEnum.TITLE,
|
||||
reward_type=4057,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=7,
|
||||
reward_category=RewardCategoryEnum.TITLE,
|
||||
reward_type=4058,
|
||||
),
|
||||
],
|
||||
),
|
||||
# SWDC
|
||||
TakeoverReward(
|
||||
takeover_reward_type=3,
|
||||
takeover_reward_data=[
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=1,
|
||||
reward_category=RewardCategoryEnum.AVATAR_BODY,
|
||||
reward_type=81,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=1,
|
||||
reward_category=RewardCategoryEnum.AVATAR_BODY,
|
||||
reward_type=189,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=2,
|
||||
reward_category=RewardCategoryEnum.FULLTUNE_TICKET,
|
||||
reward_type=1,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=2,
|
||||
reward_category=RewardCategoryEnum.AVATAR_BODY,
|
||||
reward_type=103,
|
||||
),
|
||||
TakeoverReward.TakeoverRewardData(
|
||||
reward_no=2,
|
||||
reward_category=RewardCategoryEnum.AVATAR_BODY,
|
||||
reward_type=211,
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
subcard_judge: list[SubcardJudge] = [
|
||||
SubcardJudge(
|
||||
condition_id=1,
|
||||
lower_rank=1,
|
||||
higher_rank=7,
|
||||
condition_start=17,
|
||||
condition_end=20,
|
||||
calc=3,
|
||||
),
|
||||
SubcardJudge(
|
||||
condition_id=2,
|
||||
lower_rank=1,
|
||||
higher_rank=15,
|
||||
condition_start=10,
|
||||
condition_end=10,
|
||||
calc=20,
|
||||
),
|
||||
SubcardJudge(
|
||||
condition_id=3,
|
||||
lower_rank=11,
|
||||
higher_rank=15,
|
||||
condition_start=80,
|
||||
condition_end=89,
|
||||
calc=7,
|
||||
),
|
||||
SubcardJudge(
|
||||
condition_id=3,
|
||||
lower_rank=11,
|
||||
higher_rank=15,
|
||||
condition_start=90,
|
||||
condition_end=100,
|
||||
calc=10,
|
||||
),
|
||||
SubcardJudge(
|
||||
condition_id=3,
|
||||
lower_rank=1,
|
||||
higher_rank=15,
|
||||
condition_start=0,
|
||||
condition_end=40,
|
||||
calc=-100,
|
||||
),
|
||||
SubcardJudge(
|
||||
condition_id=1,
|
||||
lower_rank=1,
|
||||
higher_rank=15,
|
||||
condition_start=21,
|
||||
condition_end=24,
|
||||
calc=7,
|
||||
),
|
||||
SubcardJudge(
|
||||
condition_id=1,
|
||||
lower_rank=1,
|
||||
higher_rank=15,
|
||||
condition_start=25,
|
||||
condition_end=25,
|
||||
calc=10,
|
||||
),
|
||||
SubcardJudge(
|
||||
condition_id=3,
|
||||
lower_rank=16,
|
||||
higher_rank=20,
|
||||
condition_start=70,
|
||||
condition_end=89,
|
||||
calc=6,
|
||||
),
|
||||
SubcardJudge(
|
||||
condition_id=3,
|
||||
lower_rank=16,
|
||||
higher_rank=20,
|
||||
condition_start=90,
|
||||
condition_end=100,
|
||||
calc=12,
|
||||
),
|
||||
SubcardJudge(
|
||||
condition_id=3,
|
||||
lower_rank=16,
|
||||
higher_rank=20,
|
||||
condition_start=0,
|
||||
condition_end=50,
|
||||
calc=-100,
|
||||
),
|
||||
]
|
||||
special_promote: list[SpecialPromote] = [
|
||||
SpecialPromote(counter=50, online_rank_id=8),
|
||||
SpecialPromote(counter=200, online_rank_id=16),
|
||||
SpecialPromote(counter=255, online_rank_id=21),
|
||||
]
|
||||
matching_id: int = 0
|
||||
matching_group: list[MatchingGroup] = [
|
||||
MatchingGroup(group_id=1, group_percent=30),
|
||||
MatchingGroup(group_id=2, group_percent=50),
|
||||
MatchingGroup(group_id=3, group_percent=100),
|
||||
]
|
||||
timetrial_disp_date: datetime = datetime(2021, 10, 1)
|
||||
# price for every car
|
||||
buy_car_need_cash: int = 5000
|
||||
# number of buyable shop/customization time limits
|
||||
time_extension_limit: int = 5
|
||||
# TODO: set based on special mode/event
|
||||
collabo_id: CollaboEnum = CollaboEnum.NONE
|
||||
# UNIX timestamp
|
||||
driver_debut_end_date: datetime = datetime(2039, 1, 1)
|
||||
online_battle_param1: int = 0
|
||||
online_battle_param2: int = 0
|
||||
online_battle_param3: int = 0
|
||||
online_battle_param4: int = 0
|
||||
online_battle_param5: int = 0
|
||||
online_battle_param6: int = 2
|
||||
online_battle_param7: int = 0
|
||||
online_battle_param8: int = 3900
|
||||
theory_open_version: str = "1.00.00"
|
||||
theory_close_version: str = "9.99.99"
|
||||
special_mode_data: Optional[SpecialModeData] = None
|
||||
timetrial_event_data: Optional[TimetrialEventData] = None
|
||||
number_lottery_data: list[NumberLotteryData] = []
|
||||
@@ -0,0 +1,34 @@
|
||||
from titles.idac.const import StoryTypeEnum
|
||||
from titles.idac.model.common_model import DtoModel
|
||||
|
||||
|
||||
class ChallengeDto(DtoModel):
|
||||
vs_type: int
|
||||
play_difficulty: int
|
||||
cleared_difficulty: int
|
||||
story_type: StoryTypeEnum
|
||||
play_count: int
|
||||
weak_difficulty: int = 0 # only used by special mode?
|
||||
eval_id: int
|
||||
advantage: int
|
||||
sec1_advantage_avg: int
|
||||
sec2_advantage_avg: int
|
||||
sec3_advantage_avg: int
|
||||
sec4_advantage_avg: int
|
||||
nearby_advantage_rate: int
|
||||
win_flag: int
|
||||
result: int
|
||||
record: int
|
||||
course_id: int
|
||||
last_play_course_id: int
|
||||
style_car_id: int
|
||||
course_day: int
|
||||
|
||||
|
||||
class BestChallengeByVsTypeDto(DtoModel):
|
||||
story_type: StoryTypeEnum
|
||||
vs_type: int
|
||||
max_clear_lv: int
|
||||
last_play_lv: int
|
||||
last_play_course_id: int
|
||||
play_count: int
|
||||
@@ -0,0 +1,103 @@
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
from pydantic import AliasChoices, BaseModel, Field
|
||||
|
||||
from titles.idac.const import StoryPlayStatusEnum, StoryTypeEnum
|
||||
from titles.idac.model.common_model import DtoModel, RequestGeneric
|
||||
|
||||
|
||||
class RequestSeason2Headers(RequestGeneric):
|
||||
timestamp: int
|
||||
a_serial: str
|
||||
a_store: int
|
||||
a_country: int
|
||||
a_region: int
|
||||
device_version: str
|
||||
asset_version: int
|
||||
|
||||
|
||||
class RequestSeason2UserHeaders(RequestSeason2Headers):
|
||||
session: int
|
||||
session_procseq: int
|
||||
|
||||
|
||||
class TipsGameModeEnum(str, Enum):
|
||||
"""Game mode types for tips tracking."""
|
||||
|
||||
TIMETRIAL = "timetrial"
|
||||
STORY = "story"
|
||||
STORE_BATTLE = "store_battle"
|
||||
ONLINE_BATTLE = "online_battle"
|
||||
SPECIAL = "special"
|
||||
CHALLENGE = "challenge"
|
||||
THEORY = "theory"
|
||||
|
||||
@property
|
||||
def field_name(self) -> str:
|
||||
"""Get the corresponding ProfileTipsDto field name."""
|
||||
return f"{self.value}_play_count"
|
||||
|
||||
|
||||
class Season2ConfigData(DtoModel):
|
||||
id: int = Field(..., validation_alias=AliasChoices("id", "config_id"))
|
||||
steering_intensity: Optional[int] = None
|
||||
transmission_type: Optional[int] = None
|
||||
default_viewpoint: Optional[int] = None
|
||||
favorite_bgm: Optional[int] = None
|
||||
bgm_volume: Optional[int] = None
|
||||
se_volume: Optional[int] = None
|
||||
master_volume: Optional[int] = None
|
||||
store_battle_policy: Optional[int] = None
|
||||
battle_onomatope_display: Optional[int] = None
|
||||
cornering_guide: Optional[int] = None
|
||||
minimap: Optional[int] = None
|
||||
line_guide: Optional[int] = None
|
||||
ghost: Optional[int] = None
|
||||
race_exit: Optional[int] = None
|
||||
result_skip: Optional[int] = None
|
||||
stamp_select_skip: Optional[int] = None
|
||||
|
||||
|
||||
class Season2TipsInfo(DtoModel):
|
||||
tips_list: str
|
||||
timetrial_play_count: int
|
||||
story_play_count: int
|
||||
store_battle_play_count: int
|
||||
online_battle_play_count: int
|
||||
special_play_count: int
|
||||
challenge_play_count: int
|
||||
theory_play_count: int
|
||||
|
||||
|
||||
class Season2StoryDataUpdate(DtoModel):
|
||||
user_id: int
|
||||
story_type: StoryTypeEnum = StoryTypeEnum.STORY
|
||||
chapter: int
|
||||
episode: int
|
||||
difficulty: int
|
||||
play_status: StoryPlayStatusEnum
|
||||
play_score: int
|
||||
chapter_clear: int
|
||||
chapter_loop_max: int
|
||||
|
||||
|
||||
class Season2StampEvent(DtoModel):
|
||||
m_stamp_event_id: int
|
||||
select_flag: int = 0
|
||||
stamp_masu: int = 0
|
||||
daily_bonus: int = 0
|
||||
weekly_bonus: int = 0
|
||||
weekday_bonus: int = 0
|
||||
weekend_bonus: int = 0
|
||||
total_bonus: int = 0
|
||||
day_total_bonus: int = 0
|
||||
store_battle_bonus: int = 0
|
||||
story_bonus: int = 0
|
||||
online_battle_bonus: int = 0
|
||||
timetrial_bonus: int = 0
|
||||
fasteststreetlegaltheory_bonus: int = 0
|
||||
collaboration_bonus: int = 0
|
||||
add_bonus_daily_flag_1: int = 0
|
||||
add_bonus_daily_flag_2: int = 0
|
||||
add_bonus_daily_flag_3: int = 0
|
||||
new_flag: bool = False
|
||||
@@ -0,0 +1,24 @@
|
||||
from titles.idac.model.season2.boot_model import ResponseSeason2BootGetConfigData
|
||||
from titles.idac.model.season3.boot_model import ResponseSeason3BootGetConfigData
|
||||
|
||||
|
||||
class StampInfoConfig(ResponseSeason2BootGetConfigData.StampInfo):
|
||||
pass
|
||||
|
||||
|
||||
class TimetrialConfig(ResponseSeason2BootGetConfigData.TimetrialEventData):
|
||||
pass
|
||||
|
||||
|
||||
class BattleGiftConfig(ResponseSeason2BootGetConfigData.BattleGiftEventMaster):
|
||||
pass
|
||||
|
||||
|
||||
class RoundConfig(ResponseSeason2BootGetConfigData.RoundEvent_2):
|
||||
pass
|
||||
|
||||
|
||||
class LeagueRewardConfig(
|
||||
ResponseSeason3BootGetConfigData.LeagueRewards,
|
||||
):
|
||||
pass
|
||||
@@ -0,0 +1,252 @@
|
||||
from datetime import datetime
|
||||
|
||||
from titles.idac.model.common_model import *
|
||||
|
||||
|
||||
class LotteryDto(DtoModel):
|
||||
saved_value: int
|
||||
lottery_count: int
|
||||
create_date: datetime
|
||||
|
||||
|
||||
class CarDto(DtoModel):
|
||||
car_id: int
|
||||
style_car_id: int
|
||||
color: int
|
||||
bureau: int
|
||||
kana: int
|
||||
s_no: int
|
||||
l_no: int
|
||||
car_flag: int
|
||||
tune_point: int
|
||||
tune_level: int = 0
|
||||
tune_parts: int
|
||||
infinity_tune: int = 0
|
||||
online_vs_win: int = 0
|
||||
pickup_seq: int = 1
|
||||
purchase_seq: int = 1
|
||||
color_stock_list: str
|
||||
color_stock_new_list: str
|
||||
parts_stock_list: str
|
||||
parts_stock_new_list: str
|
||||
parts_set_equip_list: str
|
||||
parts_list: list[Part]
|
||||
equip_parts_count: int
|
||||
use_count: int = 0
|
||||
story_use_count: int = 0
|
||||
timetrial_use_count: int = 0
|
||||
vs_use_count: int = 0
|
||||
net_vs_use_count: int = 0
|
||||
theory_use_count: int = 0
|
||||
car_mileage: int = 0
|
||||
|
||||
|
||||
class PickupOnCar(DtoModel):
|
||||
style_car_id: int
|
||||
pickup_seq: int
|
||||
|
||||
|
||||
class PickupOffCar(DtoModel):
|
||||
style_car_id: int
|
||||
|
||||
|
||||
class RequestSeason2FactoryAvatarGachaResult(RequestGeneric):
|
||||
class Reward(DtoModel):
|
||||
reward_id: int
|
||||
|
||||
avatar_gacha_id: int
|
||||
reward_data: list[Reward]
|
||||
use_ticket_cnt: int
|
||||
stock_obj: StockData
|
||||
|
||||
|
||||
class ResponseSeason2FactoryAvatarGachaResult(ResponseGeneric):
|
||||
ticket_data: list[Ticket]
|
||||
|
||||
|
||||
class RequestSeason2FactoryBuyCarResult(RequestGeneric):
|
||||
use_ticket: int
|
||||
cash: int
|
||||
total_cash: int
|
||||
car_id: int
|
||||
style_car_id: int
|
||||
color: int
|
||||
bureau: int
|
||||
kana: int
|
||||
s_no: int
|
||||
l_no: int
|
||||
car_flag: int
|
||||
tune_point: int
|
||||
tune_parts: int
|
||||
color_stock_list: str
|
||||
color_stock_new_list: str
|
||||
parts_stock_list: str
|
||||
parts_stock_new_list: str
|
||||
parts_set_equip_list: str
|
||||
purchase_seq: int
|
||||
parts_list: list[Part]
|
||||
equip_parts_count: int
|
||||
pickup_on_car_ids: list[PickupOnCar]
|
||||
pickup_off_car_ids: list[PickupOffCar]
|
||||
|
||||
|
||||
class ResponseSeason2FactoryBuyCarResult(ResponseGeneric):
|
||||
ticket_data: list[Ticket]
|
||||
fulltune_count: int
|
||||
total_car_parts_count: int
|
||||
car_layout_count: list[CarLayoutCount]
|
||||
car_style_count: list[CarStyleCount]
|
||||
|
||||
|
||||
class RequestSeason2FactorySaveFavoriteCar(RequestGeneric):
|
||||
pickup_on_car_ids: list[PickupOnCar]
|
||||
pickup_off_car_ids: list[PickupOffCar]
|
||||
|
||||
|
||||
ResponseSeason2FactorySaveFavoriteCar = ResponseGeneric
|
||||
|
||||
|
||||
class RequestSeason2FactoryUpdateCustomizeResult(RequestGeneric):
|
||||
style_car_id: int
|
||||
color: int
|
||||
color_stock_list: str
|
||||
color_stock_new_list: str
|
||||
parts_stock_list: str
|
||||
parts_stock_new_list: str
|
||||
parts_set_equip_list: str
|
||||
parts_list: list[Part]
|
||||
equip_parts_count: int
|
||||
total_car_parts_count: int
|
||||
ticket_data: list[Ticket]
|
||||
|
||||
|
||||
ResponseSeason2FactoryUpdateCustomizeResult = ResponseGeneric
|
||||
|
||||
|
||||
class RequestSeason2FactoryUpdateMultipleCustomizeResult(RequestGeneric):
|
||||
class FactoryCar(DtoModel):
|
||||
style_car_id: int
|
||||
color: int
|
||||
color_stock_list: str
|
||||
color_stock_new_list: str
|
||||
parts_stock_list: str
|
||||
parts_stock_new_list: str
|
||||
parts_set_equip_list: str
|
||||
parts_list: list[Part]
|
||||
equip_parts_count: int
|
||||
|
||||
car_list: list[FactoryCar]
|
||||
total_car_parts_count: int
|
||||
ticket_data: list[Ticket]
|
||||
|
||||
|
||||
ResponseSeason2FactoryUpdateMultipleCustomizeResult = ResponseGeneric
|
||||
|
||||
|
||||
class RequestSeason2FactoryUpdateCustomizeUser(RequestGeneric):
|
||||
mytitle_id: int
|
||||
mytitle_efffect_id: int
|
||||
papercup_id: int
|
||||
tachometer_id: int
|
||||
aura_id: int
|
||||
aura_color_id: int
|
||||
aura_line_id: int
|
||||
bgm_id: int
|
||||
keyholder_id: int
|
||||
start_menu_bg_id: int
|
||||
stamp_key_assign_0: int
|
||||
stamp_key_assign_1: int
|
||||
stamp_key_assign_2: int
|
||||
stamp_key_assign_3: int
|
||||
stock_obj: StockData
|
||||
|
||||
|
||||
ResponseSeason2FactoryUpdateCustomizeUser = ResponseGeneric
|
||||
|
||||
|
||||
class RequestSeason2FactoryUpdateCustomizeAvatar(RequestGeneric):
|
||||
stock_obj: StockData
|
||||
avatar_obj: AvatarData
|
||||
|
||||
|
||||
ResponseSeason2FactoryUpdateCustomizeAvatar = ResponseGeneric
|
||||
|
||||
|
||||
RequestSeason2FactoryRenameBefore = RequestGeneric
|
||||
|
||||
ResponseSeason2FactoryRenameBefore = ResponseGeneric
|
||||
|
||||
|
||||
class RequestSeason2FactoryRenameResult(RequestGeneric):
|
||||
username: str
|
||||
|
||||
|
||||
ResponseSeason2FactoryRenameResult = ResponseGeneric
|
||||
|
||||
RequestSeason2FactoryGetCarData = RequestGeneric
|
||||
|
||||
|
||||
class ResponseSeason2FactoryGetCarData(ResponseGeneric):
|
||||
class FactoryCarData(DtoModel):
|
||||
id: int
|
||||
car_id: int
|
||||
style_car_id: int
|
||||
color: int
|
||||
bureau: int
|
||||
kana: int
|
||||
s_no: int
|
||||
l_no: int
|
||||
car_flag: int
|
||||
tune_point: int
|
||||
tune_parts: int
|
||||
infinity_tune: int
|
||||
online_vs_win: int
|
||||
color_stock_list: str
|
||||
color_stock_new_list: str
|
||||
parts_stock_list: str
|
||||
parts_stock_new_list: str
|
||||
parts_set_equip_list: str
|
||||
use_count: int
|
||||
story_use_count: int
|
||||
timetrial_use_count: int
|
||||
vs_use_count: int
|
||||
net_vs_use_count: int
|
||||
theory_use_count: int
|
||||
car_mileage: int
|
||||
pickup_seq: int
|
||||
purchase_seq: int
|
||||
parts_list: list[Part]
|
||||
equip_parts_count: int
|
||||
|
||||
car_data: list[FactoryCarData]
|
||||
|
||||
|
||||
RequestSeason2FactoryNumberLotteryBefore = RequestGeneric
|
||||
|
||||
|
||||
class ResponseSeason2FactoryNumberLotteryBefore(ResponseGeneric):
|
||||
class LotteryInfo(DtoModel):
|
||||
class LotteryWin(DtoModel):
|
||||
m_number_lottery_schedule_no: int
|
||||
win_number: int
|
||||
|
||||
lottery_count: int
|
||||
win_list: list[LotteryWin]
|
||||
|
||||
lottery_info: LotteryInfo
|
||||
|
||||
|
||||
class RequestSeason2FactoryNumberLotteryResult(RequestGeneric):
|
||||
style_car_id: int
|
||||
l_no: int
|
||||
win_number: int
|
||||
lottery_count: int
|
||||
isEnd: int
|
||||
cash: int
|
||||
total_cash: int
|
||||
dressup_point: int
|
||||
avatar_point: int
|
||||
ticket_data: list[Ticket]
|
||||
|
||||
|
||||
ResponseSeason2FactoryNumberLotteryResult = ResponseGeneric
|
||||
@@ -0,0 +1,44 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from titles.idac.const import BattleGiftStatusEnum, RewardCategoryEnum
|
||||
from titles.idac.model.common_model import DtoModel
|
||||
|
||||
|
||||
class TicketDto(DtoModel):
|
||||
ticket_id: RewardCategoryEnum
|
||||
ticket_cnt: int
|
||||
|
||||
|
||||
class StampDto(DtoModel):
|
||||
m_stamp_event_id: int
|
||||
select_flag: int
|
||||
stamp_masu: int
|
||||
daily_bonus: int
|
||||
weekly_bonus: int
|
||||
weekday_bonus: int
|
||||
weekend_bonus: int
|
||||
total_bonus: int
|
||||
day_total_bonus: int
|
||||
store_battle_bonus: int
|
||||
story_bonus: int
|
||||
online_battle_bonus: int
|
||||
timetrial_bonus: int
|
||||
fasteststreetlegaltheory_bonus: int
|
||||
collaboration_bonus: int
|
||||
add_bonus_daily_flag_1: int
|
||||
add_bonus_daily_flag_2: int
|
||||
add_bonus_daily_flag_3: int
|
||||
create_date_daily: Optional[datetime] = None
|
||||
create_date_weekly: Optional[datetime] = None
|
||||
|
||||
|
||||
class TimeTrialEventDto(DtoModel):
|
||||
timetrial_event_id: int
|
||||
point: int
|
||||
|
||||
|
||||
class BattleGiftDto(DtoModel):
|
||||
battle_gift_event_id: int
|
||||
gift_id: int
|
||||
gift_get_status: BattleGiftStatusEnum = BattleGiftStatusEnum.AQUIRED
|
||||
received_date: Optional[datetime] = None
|
||||
@@ -0,0 +1,112 @@
|
||||
from datetime import datetime
|
||||
from titles.idac.model.common_model import DtoModel, RequestGeneric, ResponseGeneric
|
||||
|
||||
|
||||
class RequestSeason2LogSavePlayLog(RequestGeneric):
|
||||
class PickupCarData(DtoModel):
|
||||
style_car_id: int
|
||||
point: int
|
||||
|
||||
class GeneralLog(DtoModel):
|
||||
param: int
|
||||
|
||||
class MatchErrorInfo(DtoModel):
|
||||
group_key: str
|
||||
error_sequence_id: int
|
||||
communication_type_jadge_flag: int
|
||||
opponent_store: int
|
||||
|
||||
board_id: str
|
||||
seat_no: int
|
||||
mode_id: int
|
||||
course_id: int
|
||||
course_day: int
|
||||
style_car_id: int
|
||||
pickup_car_data: list[PickupCarData]
|
||||
story_type: int
|
||||
chapter: int
|
||||
episode: int
|
||||
difficulty: int
|
||||
play_score: int
|
||||
record: int
|
||||
mode_exp: int
|
||||
mode_rank: int
|
||||
continue_flag: int
|
||||
continue_ticket_use_flag: int
|
||||
grade: int
|
||||
use_zenkoku_intro: int
|
||||
start_dt: datetime
|
||||
end_dt: datetime
|
||||
mode_select_start_date: datetime
|
||||
result_start_date: datetime
|
||||
name_entry_sec: int
|
||||
game_start_sec: int
|
||||
mode_select_sec: int
|
||||
total_course_select_sec: int
|
||||
total_car_select_sec: int
|
||||
play_course_select_sec: int
|
||||
play_car_select_sec: int
|
||||
shop_stay_sec: int
|
||||
matching_success_count: int
|
||||
result_sec: int
|
||||
transmission: int
|
||||
start_ui_select: int
|
||||
start_ui_decision: int
|
||||
mode_ui_select: int
|
||||
mode_ui_decision: int
|
||||
course_ui_select: int
|
||||
course_ui_decision: int
|
||||
inshop_standby_status: int
|
||||
new_player: int
|
||||
standby_play_flag: int
|
||||
use_credit: int
|
||||
viewpoint: int
|
||||
retire_type: int
|
||||
timetrial_eval: int
|
||||
bgm_id: int
|
||||
invade_invite_flag: int
|
||||
invade_invite_select_flag: int
|
||||
first_free_flag: int
|
||||
coin_to_credit: int
|
||||
ind_run_general_log: list[GeneralLog]
|
||||
app_general_log: list[GeneralLog]
|
||||
group_key: str
|
||||
rematch_count: int
|
||||
race_end_flag: int
|
||||
communication_type: int
|
||||
match_error_info_arr: list[MatchErrorInfo]
|
||||
|
||||
|
||||
ResponseSeason2LogSavePlayLog = ResponseGeneric
|
||||
|
||||
|
||||
class RequestSeason2LogSaveEndLog(RequestGeneric):
|
||||
board_id: str
|
||||
seat_no: int
|
||||
story_play_cnt: int
|
||||
expedition_play_cnt: int
|
||||
timetrial_play_cnt: int
|
||||
vs_play_cnt: int
|
||||
net_vs_play_cnt: int
|
||||
theory_play_cnt: int
|
||||
buy_car_cnt: int
|
||||
start_credit: int
|
||||
continue_credit: int
|
||||
buy_car_credit: int
|
||||
extend_time_credit: int
|
||||
rename_credit: int
|
||||
total_play_credit_cnt: int
|
||||
total_buy_car_credit_cnt: int
|
||||
continue_ticket_use_cnt: int
|
||||
buy_car_ticket_use_cnt: int
|
||||
car_id: int
|
||||
style_car_id: int
|
||||
replay_mode: int
|
||||
cash_use_cnt: int
|
||||
avatar_ticket_use_cnt: int
|
||||
custom_ticket_use_cnt: int
|
||||
extend_time_cnt: int
|
||||
rename_cnt: int
|
||||
|
||||
|
||||
ResponseSeason2LogSaveEndLog = ResponseGeneric
|
||||
@@ -0,0 +1,65 @@
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel
|
||||
|
||||
from titles.idac.model.common_model import (
|
||||
LockResultEnum,
|
||||
RequestGeneric,
|
||||
ResponseGeneric,
|
||||
)
|
||||
|
||||
|
||||
class RequestSeason2LoginCheckLock(RequestGeneric):
|
||||
id: int
|
||||
accesscode: str
|
||||
device_name: str
|
||||
|
||||
|
||||
class ResponseSeason2LoginCheckLock(ResponseGeneric):
|
||||
class TakeoverData(BaseModel):
|
||||
takeover_user_name: str
|
||||
takeover_extract_day: str
|
||||
drv_level_class: int
|
||||
drv_level: int
|
||||
pride_level: int
|
||||
swdc_drv_level_class: int
|
||||
swdc_drv_level: int
|
||||
full_spec_car_num_class: int
|
||||
full_spec_car_num: int
|
||||
mileage_class: int
|
||||
mileage: int
|
||||
gamever: int
|
||||
|
||||
lock_result: LockResultEnum
|
||||
lock_date: datetime
|
||||
daily_play: int # double check this
|
||||
session: str
|
||||
shared_security_key: str
|
||||
session_procseq: int
|
||||
new_player: int
|
||||
server_status: int
|
||||
takeover_data: list[TakeoverData]
|
||||
|
||||
|
||||
RequestSeason2LoginUnLock = RequestGeneric
|
||||
|
||||
|
||||
class ResponseSeason2LoginUnLock(ResponseGeneric):
|
||||
lock_result: LockResultEnum
|
||||
|
||||
|
||||
RequestSeason2LoginReLock = RequestGeneric
|
||||
|
||||
|
||||
class ResponseSeason2LoginReLock(ResponseGeneric):
|
||||
lock_result: LockResultEnum
|
||||
lock_date: int
|
||||
|
||||
|
||||
class RequestSeason2LoginGuestPlay(RequestGeneric):
|
||||
device_name: str
|
||||
|
||||
|
||||
class ResponseSeason2LoginGuestPlay(ResponseGeneric):
|
||||
session: str
|
||||
shared_security_key: str
|
||||
session_procseq: int
|
||||
@@ -0,0 +1,38 @@
|
||||
from titles.idac.model.common_model import *
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateTimeTrialResult(RequestGeneric):
|
||||
goal_time: int
|
||||
section_time_1: int
|
||||
section_time_2: int
|
||||
section_time_3: int
|
||||
section_time_4: int
|
||||
style_car_id: int
|
||||
car_mileage: int
|
||||
course_id: int
|
||||
skill_level_exp: int
|
||||
mission: int
|
||||
mileage: int
|
||||
eval_id: int
|
||||
event_point: int
|
||||
stock_obj: StockData
|
||||
aura_id: int
|
||||
aura_color_id: int
|
||||
aura_line_id: int
|
||||
cash: int
|
||||
total_cash: int
|
||||
dressup_point: int
|
||||
avatar_point: int
|
||||
ticket_data: list[Ticket]
|
||||
reward_dist_obj: RewardDist
|
||||
driver_debut_obj: DriverDebut
|
||||
mode_rank_obj: ModeRank
|
||||
|
||||
|
||||
class ResponseSeason2UserUpdateTimeTrialResult(ResponseGeneric):
|
||||
course_rank: int
|
||||
course_car_rank: int
|
||||
location_course_store_rank: int
|
||||
car_use_count: list[CarUseCount]
|
||||
maker_use_count: list[MakerUseCount]
|
||||
timetrial_event_data: TimetrialEventData_2
|
||||
@@ -0,0 +1,22 @@
|
||||
from datetime import datetime
|
||||
|
||||
from titles.idac.model.common_model import DtoModel
|
||||
|
||||
|
||||
class RoundInfoDto(DtoModel):
|
||||
round_id_in_json: int
|
||||
name: str
|
||||
start_dt: datetime
|
||||
end_dt: datetime
|
||||
|
||||
|
||||
class UserRoundInfoDto(DtoModel):
|
||||
round_id: int
|
||||
count: int = 0
|
||||
win: int = 0
|
||||
point: int = 0
|
||||
play_dt: datetime
|
||||
|
||||
|
||||
class UserRoundInfoWithRankDto(UserRoundInfoDto):
|
||||
rank: int
|
||||
@@ -0,0 +1,23 @@
|
||||
from titles.idac.const import StoryTypeEnum
|
||||
from titles.idac.model.common_model import DtoModel
|
||||
|
||||
|
||||
class StoryDto(DtoModel):
|
||||
story_type: StoryTypeEnum = StoryTypeEnum.STORY
|
||||
chapter: int
|
||||
loop_count: int
|
||||
|
||||
|
||||
class StoryEpisodeDto(DtoModel):
|
||||
chapter: int
|
||||
episode: int
|
||||
play_status: int
|
||||
|
||||
|
||||
class StoryEpisodeDifficultyDto(DtoModel):
|
||||
episode: int
|
||||
difficulty: int
|
||||
play_count: int = 0
|
||||
clear_count: int
|
||||
play_status: int
|
||||
play_score: int
|
||||
@@ -0,0 +1,111 @@
|
||||
from datetime import datetime
|
||||
|
||||
from titles.idac.model.common_model import DtoModel, RequestGeneric, ResponseGeneric
|
||||
from titles.idac.model.season2.factory_model import CarDto
|
||||
from titles.idac.model.season2.user_model import (
|
||||
ProfileAvatarDto,
|
||||
ProfileDto,
|
||||
ProfileRankDto,
|
||||
)
|
||||
|
||||
|
||||
class TheoryRivalDto(DtoModel):
|
||||
profile: ProfileDto
|
||||
avatar: ProfileAvatarDto
|
||||
car: CarDto
|
||||
grade: int
|
||||
|
||||
|
||||
class TheoryCourseDto(DtoModel):
|
||||
user: int
|
||||
course_id: int
|
||||
max_victory_grade: int
|
||||
run_count: int
|
||||
powerhouse_lv: int
|
||||
powerhouse_exp: int
|
||||
played_powerhouse_lv: int
|
||||
update_dt: datetime
|
||||
|
||||
|
||||
class TheoryPartnerDto(DtoModel):
|
||||
partner_id: int
|
||||
fellowship_lv: int
|
||||
fellowship_exp: int
|
||||
|
||||
|
||||
class TheoryRunningDto(DtoModel):
|
||||
course_id: int
|
||||
attack: int
|
||||
defense: int
|
||||
safety: int
|
||||
runaway: int
|
||||
trick_flag: int
|
||||
|
||||
|
||||
class TheoryProfileDto(DtoModel):
|
||||
user: int
|
||||
grade: int = 0
|
||||
|
||||
|
||||
class RequestSeason2TheoryMatching(RequestGeneric):
|
||||
course_id: int
|
||||
grade: int
|
||||
powerhouse_lv: int
|
||||
|
||||
|
||||
class Season2PowerMatch(DtoModel):
|
||||
id: int
|
||||
name: str
|
||||
grade: int
|
||||
powerhouseLv: int
|
||||
mytitleId: int
|
||||
country: int
|
||||
auraId: int
|
||||
auraColor: int
|
||||
auraLine: int
|
||||
roundRanking: int
|
||||
storeName: str
|
||||
sex: int
|
||||
face: int
|
||||
eye: int
|
||||
mouth: int
|
||||
hair: int
|
||||
glasses: int
|
||||
faceAccessory: int
|
||||
body: int
|
||||
bodyAccessory: int
|
||||
behind: int
|
||||
bg: int
|
||||
effect: int
|
||||
special: int
|
||||
styleCarId: int
|
||||
color: int
|
||||
bureau: int
|
||||
kana: int
|
||||
sNo: int
|
||||
lNo: int
|
||||
tuneLv: int
|
||||
carFlag: int
|
||||
tunePoint: int
|
||||
infinityTune: int
|
||||
tuneParts: int
|
||||
partsList: list[int]
|
||||
partsCount: int
|
||||
stamp0: int
|
||||
stamp1: int
|
||||
stamp2: int
|
||||
stamp3: int
|
||||
attack: int = 128
|
||||
defense: int = 128
|
||||
safety: int = 128
|
||||
runaway: int = 128
|
||||
trickFlg: int = 0
|
||||
taEval: int
|
||||
|
||||
|
||||
class ResponseSeason2TheoryMatching(ResponseGeneric):
|
||||
class RivalData(DtoModel):
|
||||
auto_match: list[Season2PowerMatch] = []
|
||||
power_match: list[Season2PowerMatch] = []
|
||||
|
||||
rival_data: RivalData
|
||||
@@ -0,0 +1,95 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import Field
|
||||
from titles.idac.model.common_model import DtoModel, RequestGeneric, ResponseGeneric
|
||||
|
||||
|
||||
class CourseDto(DtoModel):
|
||||
course_id: int
|
||||
run_counts: int
|
||||
skill_level_exp: int
|
||||
|
||||
|
||||
class TrialDto(DtoModel):
|
||||
user: int
|
||||
style_car_id: int
|
||||
course_id: int
|
||||
eval_id: int
|
||||
goal_time: int
|
||||
section_time_1: int
|
||||
section_time_2: int
|
||||
section_time_3: int
|
||||
section_time_4: int
|
||||
mission: int
|
||||
play_dt: datetime
|
||||
rank: Optional[int] = None
|
||||
total: Optional[int] = None
|
||||
|
||||
|
||||
class CourseBestData(DtoModel):
|
||||
course_id: int
|
||||
rank: int
|
||||
member: int = Field(alias="user")
|
||||
value: int = Field(alias="goal_time")
|
||||
total: Optional[int] = None
|
||||
store: str = ""
|
||||
style_car_id: int
|
||||
play_dt: datetime
|
||||
section_time_1: int
|
||||
section_time_2: int
|
||||
section_time_3: int
|
||||
section_time_4: int
|
||||
mission: int = 0
|
||||
|
||||
|
||||
class CourseBestCarData(DtoModel):
|
||||
class CarList(DtoModel):
|
||||
rank: int
|
||||
member: int = Field(alias="user")
|
||||
value: int = Field(alias="goal_time")
|
||||
total: Optional[int] = None
|
||||
store: str
|
||||
style_car_id: int
|
||||
play_dt: datetime
|
||||
section_time_1: int
|
||||
section_time_2: int
|
||||
section_time_3: int
|
||||
section_time_4: int
|
||||
mission: int
|
||||
|
||||
course_id: int
|
||||
car_list: list[CarList]
|
||||
|
||||
|
||||
class CarId(DtoModel):
|
||||
car_id: int
|
||||
style_car_id: int
|
||||
|
||||
|
||||
class RequestSeason2TimeTrialGetBestRecordPreRace(RequestGeneric):
|
||||
car_ids: list[CarId]
|
||||
course_id: int
|
||||
|
||||
|
||||
class ResponseSeason2TimeTrialGetBestRecordPreRace(ResponseGeneric):
|
||||
course_car_best_data: list[CourseBestCarData]
|
||||
course_best_data: list[CourseBestData]
|
||||
|
||||
|
||||
class RequestSeason2TimeTrialGetBestRecordPreBattle(RequestGeneric):
|
||||
car_ids: list[CarId]
|
||||
|
||||
|
||||
class ResponseSeason2TimeTrialGetBestRecordPreBattle(ResponseGeneric):
|
||||
course_pickup_car_best_data: list[CourseBestCarData]
|
||||
|
||||
|
||||
RequestSeason2TimeTrialGetBestRecordPreTA = (
|
||||
RequestSeason2TimeTrialGetBestRecordPreBattle
|
||||
)
|
||||
|
||||
|
||||
class ResponseSeason2TimeTrialGetBestRecordPreTA(ResponseGeneric):
|
||||
course_mybest_data: list[CourseBestData]
|
||||
course_pickup_car_best_data: list[CourseBestCarData]
|
||||
@@ -0,0 +1,881 @@
|
||||
from typing import Optional
|
||||
from datetime import datetime, timezone
|
||||
from pydantic import BaseModel, field_validator
|
||||
|
||||
from titles.idac.const import NameChangeCategoryEnum, StoryTypeEnum
|
||||
from titles.idac.model.common_model import *
|
||||
from titles.idac.model.season2.common_model import (
|
||||
Season2ConfigData,
|
||||
Season2StampEvent,
|
||||
Season2TipsInfo,
|
||||
)
|
||||
from titles.idac.model.season2.item_model import TicketDto
|
||||
|
||||
|
||||
class ProfileDto(DtoModel):
|
||||
version: Optional[int] = None
|
||||
username: Optional[str] = None
|
||||
country: Optional[int] = None
|
||||
store: Optional[int] = None
|
||||
total_play: Optional[int] = None
|
||||
daily_play: Optional[int] = None
|
||||
day_play: Optional[int] = None
|
||||
mileage: Optional[int] = None
|
||||
device_version: Optional[str] = None
|
||||
last_play_date: Optional[datetime] = None
|
||||
mytitle_id: Optional[int] = None
|
||||
mytitle_efffect_id: Optional[int] = None
|
||||
sticker_id: Optional[int] = None
|
||||
sticker_effect_id: Optional[int] = None
|
||||
papercup_id: Optional[int] = None
|
||||
tachometer_id: Optional[int] = None
|
||||
aura_id: Optional[int] = None
|
||||
aura_color_id: Optional[int] = None
|
||||
aura_line_id: Optional[int] = None
|
||||
bgm_id: Optional[int] = None
|
||||
keyholder_id: Optional[int] = None
|
||||
start_menu_bg_id: Optional[int] = None
|
||||
use_car_id: Optional[int] = None
|
||||
use_style_car_id: Optional[int] = None
|
||||
bothwin_count: Optional[int] = None
|
||||
bothwin_score: Optional[int] = None
|
||||
subcard_count: Optional[int] = None
|
||||
vs_history: Optional[int] = None
|
||||
stamp_key_assign_0: Optional[int] = None
|
||||
stamp_key_assign_1: Optional[int] = None
|
||||
stamp_key_assign_2: Optional[int] = None
|
||||
stamp_key_assign_3: Optional[int] = None
|
||||
name_change_category: Optional[NameChangeCategoryEnum] = None
|
||||
factory_disp: Optional[int] = None
|
||||
create_date: Optional[datetime] = None
|
||||
cash: Optional[int] = None
|
||||
dressup_point: Optional[int] = None
|
||||
avatar_point: Optional[int] = None
|
||||
total_cash: Optional[int] = None
|
||||
total_car_parts_count: Optional[int] = None
|
||||
|
||||
|
||||
class ProfileConfigDto(DtoModel):
|
||||
steering_intensity: Optional[int] = None
|
||||
transmission_type: Optional[int] = None
|
||||
default_viewpoint: Optional[int] = None
|
||||
favorite_bgm: Optional[int] = None
|
||||
favorite_bgm_list: Optional[str] = None
|
||||
bgm_volume: Optional[int] = None
|
||||
se_volume: Optional[int] = None
|
||||
master_volume: Optional[int] = None
|
||||
store_battle_policy: Optional[int] = None
|
||||
battle_onomatope_display: Optional[int] = None
|
||||
cornering_guide: Optional[int] = None
|
||||
minimap: Optional[int] = None
|
||||
line_guide: Optional[int] = None
|
||||
ghost: Optional[int] = None
|
||||
race_exit: Optional[int] = None
|
||||
result_skip: Optional[int] = None
|
||||
stamp_select_skip: Optional[int] = None
|
||||
break_flag: Optional[int] = None
|
||||
visible_my_stats: Optional[int] = None
|
||||
visible_rival_stats: Optional[int] = None
|
||||
|
||||
|
||||
ProfileAvatarDto = AvatarData
|
||||
|
||||
|
||||
class ProfileRankDto(DtoModel):
|
||||
story_rank_exp: int = 0
|
||||
story_rank: int = 1
|
||||
time_trial_rank_exp: int = 0
|
||||
time_trial_rank: int = 1
|
||||
online_battle_rank_exp: int = 0
|
||||
online_battle_rank: int = 1
|
||||
store_battle_rank_exp: int = 0
|
||||
store_battle_rank: int = 1
|
||||
theory_exp: int = 0
|
||||
theory_rank: int = 1
|
||||
pride_group_id: int = 0
|
||||
pride_point: int = 0
|
||||
grade_exp: int = 0
|
||||
grade: int = 1
|
||||
grade_reward_dist: int = 0
|
||||
story_rank_reward_dist: int = 0
|
||||
time_trial_rank_reward_dist: int = 0
|
||||
online_battle_rank_reward_dist: int = 0
|
||||
store_battle_rank_reward_dist: int = 0
|
||||
theory_rank_reward_dist: int = 0
|
||||
max_attained_online_battle_rank: int = 0
|
||||
max_attained_pride_point: int = 0
|
||||
is_last_max: int = 0
|
||||
|
||||
|
||||
class ProfileStockDto(DtoModel):
|
||||
@field_validator("*", mode="before")
|
||||
def empty_string_to_none(cls, v):
|
||||
"""Converts all empty strings to NOne so they do not get updated in the db"""
|
||||
if v == "":
|
||||
return None
|
||||
return v
|
||||
|
||||
mytitle_list: Optional[str] = None
|
||||
mytitle_new_list: Optional[str] = None
|
||||
avatar_face_list: Optional[str] = None
|
||||
avatar_face_new_list: Optional[str] = None
|
||||
avatar_eye_list: Optional[str] = None
|
||||
avatar_eye_new_list: Optional[str] = None
|
||||
avatar_hair_list: Optional[str] = None
|
||||
avatar_hair_new_list: Optional[str] = None
|
||||
avatar_body_list: Optional[str] = None
|
||||
avatar_body_new_list: Optional[str] = None
|
||||
avatar_mouth_list: Optional[str] = None
|
||||
avatar_mouth_new_list: Optional[str] = None
|
||||
avatar_glasses_list: Optional[str] = None
|
||||
avatar_glasses_new_list: Optional[str] = None
|
||||
avatar_face_accessory_list: Optional[str] = None
|
||||
avatar_face_accessory_new_list: Optional[str] = None
|
||||
avatar_body_accessory_list: Optional[str] = None
|
||||
avatar_body_accessory_new_list: Optional[str] = None
|
||||
avatar_behind_list: Optional[str] = None
|
||||
avatar_behind_new_list: Optional[str] = None
|
||||
avatar_bg_list: Optional[str] = None
|
||||
avatar_bg_new_list: Optional[str] = None
|
||||
avatar_effect_list: Optional[str] = None
|
||||
avatar_effect_new_list: Optional[str] = None
|
||||
avatar_special_list: Optional[str] = None
|
||||
avatar_special_new_list: Optional[str] = None
|
||||
stamp_list: Optional[str] = None
|
||||
stamp_new_list: Optional[str] = None
|
||||
keyholder_list: Optional[str] = None
|
||||
keyholder_new_list: Optional[str] = None
|
||||
papercup_list: Optional[str] = None
|
||||
papercup_new_list: Optional[str] = None
|
||||
tachometer_list: Optional[str] = None
|
||||
tachometer_new_list: Optional[str] = None
|
||||
aura_list: Optional[str] = None
|
||||
aura_new_list: Optional[str] = None
|
||||
aura_color_list: Optional[str] = None
|
||||
aura_color_new_list: Optional[str] = None
|
||||
aura_line_list: Optional[str] = None
|
||||
aura_line_new_list: Optional[str] = None
|
||||
bgm_list: Optional[str] = None
|
||||
bgm_new_list: Optional[str] = None
|
||||
dx_color_list: Optional[str] = None
|
||||
dx_color_new_list: Optional[str] = None
|
||||
start_menu_bg_list: Optional[str] = None
|
||||
start_menu_bg_new_list: Optional[str] = None
|
||||
under_neon_list: Optional[str] = None
|
||||
|
||||
|
||||
class ProfileTheoryDto(DtoModel):
|
||||
play_count: int = 0
|
||||
play_count_multi: int = 0
|
||||
partner_id: int
|
||||
partner_progress: int
|
||||
partner_progress_score: int
|
||||
practice_start_rank: int = 0
|
||||
general_flag: int = 0
|
||||
vs_history: int = 0
|
||||
vs_history_multi: int = 0
|
||||
win_count: int = 0
|
||||
win_count_multi: int = 0
|
||||
|
||||
|
||||
class ProfileTipsDto(DtoModel):
|
||||
tips_list: Optional[str] = None
|
||||
timetrial_play_count: Optional[int] = None
|
||||
story_play_count: Optional[int] = None
|
||||
store_battle_play_count: Optional[int] = None
|
||||
online_battle_play_count: Optional[int] = None
|
||||
special_play_count: Optional[int] = None
|
||||
challenge_play_count: Optional[int] = None
|
||||
theory_play_count: Optional[int] = None
|
||||
# added in season 3
|
||||
last_team_join_dt: Optional[datetime] = None
|
||||
|
||||
|
||||
class SeasonTimetrialData(DtoModel):
|
||||
style_car_id: int = 0
|
||||
course_id: int
|
||||
skill_level_exp: int
|
||||
goal_time: int = 0
|
||||
rank: int = 0
|
||||
rank_dt: datetime = datetime.fromtimestamp(0, tz=timezone.utc)
|
||||
|
||||
class SeasonTimetrialDto(SeasonTimetrialData):
|
||||
eval_id: int = 0
|
||||
|
||||
|
||||
class StoryDataDto(DtoModel):
|
||||
class EpisodeDto(DtoModel):
|
||||
class DifficultyDto(DtoModel):
|
||||
difficulty: int
|
||||
play_count: int = 0
|
||||
clear_count: int = 0
|
||||
play_status: StoryPlayStatusEnum = StoryPlayStatusEnum.NEW
|
||||
play_score: int = 0
|
||||
|
||||
episode: int
|
||||
play_status: StoryPlayStatusEnum = StoryPlayStatusEnum.NEW
|
||||
difficulty_data: list[DifficultyDto] = []
|
||||
needs_update: bool = False
|
||||
|
||||
story_type: StoryTypeEnum = StoryTypeEnum.STORY
|
||||
chapter: int
|
||||
loop_count: int = 1
|
||||
episode_data: list[EpisodeDto] = []
|
||||
needs_update: bool = False
|
||||
|
||||
|
||||
class RewardDistDto(DtoModel):
|
||||
grade_reward_dist: int
|
||||
story_rank_reward_dist: int
|
||||
time_trial_rank_reward_dist: int
|
||||
online_battle_rank_reward_dist: int
|
||||
store_battle_rank_reward_dist: int
|
||||
theory_rank_reward_dist: int
|
||||
|
||||
|
||||
class UpdateStoryResultRequest(DtoModel):
|
||||
story_type: StoryTypeEnum = StoryTypeEnum.STORY
|
||||
chapter: int
|
||||
episode: int
|
||||
difficulty: int
|
||||
play_status: int
|
||||
play_score: int
|
||||
chapter_clear: int
|
||||
chapter_loop_max: int
|
||||
course_id: int
|
||||
mileage: int
|
||||
stock_obj: ProfileStockDto
|
||||
aura_id: int
|
||||
aura_color_id: int
|
||||
aura_line_id: int
|
||||
cash: int
|
||||
total_cash: int
|
||||
dressup_point: int
|
||||
avatar_point: int
|
||||
ticket_data: list[TicketDto]
|
||||
mode_rank_obj: ProfileRankDto
|
||||
reward_dist_obj: RewardDistDto
|
||||
driver_debut_obj: DriverDebut
|
||||
|
||||
|
||||
class RequestSeason2UserCreateAccount(RequestGeneric):
|
||||
class CarObj(DtoModel):
|
||||
car_id: int
|
||||
style_car_id: int
|
||||
color: int
|
||||
bureau: int
|
||||
kana: int
|
||||
s_no: int
|
||||
l_no: int
|
||||
car_flag: int
|
||||
tune_point: int
|
||||
tune_parts: int
|
||||
color_stock_list: str
|
||||
color_stock_new_list: str
|
||||
parts_stock_list: str
|
||||
parts_stock_new_list: str
|
||||
parts_set_equip_list: str
|
||||
parts_list: list[Part]
|
||||
equip_parts_count: int
|
||||
|
||||
username: str
|
||||
car_obj: CarObj
|
||||
avatar_obj: AvatarData
|
||||
config_obj: Season2ConfigData
|
||||
stamp_key_assign_0: int
|
||||
stamp_key_assign_1: int
|
||||
stamp_key_assign_2: int
|
||||
stamp_key_assign_3: int
|
||||
mode_rank_data: ModeRankData
|
||||
takeover_stock_obj: StockData
|
||||
takeover_ticket: list[Ticket]
|
||||
cash: int
|
||||
use_ticket: int
|
||||
|
||||
|
||||
class ResponseSeason2UserCreateAccount(ResponseGeneric):
|
||||
round_event: list[RoundEvent] = []
|
||||
past_round_event: list[PastRoundEvent] = []
|
||||
stamp_event_data: list[StampEventData] = []
|
||||
fulltune_count: int = 0
|
||||
total_car_parts_count: int = 0
|
||||
car_layout_count: list[CarLayoutCount]
|
||||
car_style_count: list[CarStyleCount]
|
||||
battle_gift_data: list[BattleGift] = []
|
||||
|
||||
|
||||
class RequestSeason2UserGetData(RequestGeneric):
|
||||
battle_gift_event_id: int
|
||||
|
||||
|
||||
class ResponseSeason2UserGetData(ResponseGeneric):
|
||||
class UserBaseData(DtoModel):
|
||||
id: int
|
||||
username: str
|
||||
access_code: str
|
||||
country: int
|
||||
store: int
|
||||
total_play: int
|
||||
daily_play: int
|
||||
day_play: int
|
||||
mileage: int
|
||||
device_version: str
|
||||
asset_version: int
|
||||
last_play_dt: datetime = Field(alias="last_play_date")
|
||||
mytitle_id: int
|
||||
mytitle_efffect_id: int
|
||||
sticker_id: int
|
||||
sticker_effect_id: int
|
||||
papercup_id: int
|
||||
tachometer_id: int
|
||||
aura_id: int
|
||||
aura_color_id: int
|
||||
aura_line_id: int
|
||||
bgm_id: int
|
||||
keyholder_id: int
|
||||
start_menu_bg_id: int
|
||||
use_car_id: int
|
||||
use_style_car_id: int
|
||||
bothwin_count: int
|
||||
bothwin_score: int
|
||||
subcard_count: int
|
||||
vs_history: int
|
||||
stamp_key_assign_0: int
|
||||
stamp_key_assign_1: int
|
||||
stamp_key_assign_2: int
|
||||
stamp_key_assign_3: int
|
||||
name_change_category: int
|
||||
factory_disp: int
|
||||
create_dt: datetime = Field(alias="create_date")
|
||||
store_name: str
|
||||
mode_rank_data: ModeRankData
|
||||
have_car_cnt: int
|
||||
cash: int
|
||||
dressup_point: int
|
||||
avatar_point: int
|
||||
total_cash: int
|
||||
|
||||
class MissionData(DtoModel):
|
||||
id: int
|
||||
achieve_flag: int
|
||||
received_flag: int
|
||||
update_dt: datetime
|
||||
|
||||
class WeeklyMission(DtoModel):
|
||||
id: int
|
||||
internal_index: int
|
||||
mission_no: int
|
||||
reward_no: int
|
||||
progress: int
|
||||
|
||||
class CourseData(DtoModel):
|
||||
id: int
|
||||
course_id: int
|
||||
run_counts: int
|
||||
skill_level_exp: int
|
||||
|
||||
class ToppatuEventData(DtoModel):
|
||||
id: int
|
||||
event_id: int
|
||||
count1: int
|
||||
count2: int
|
||||
count3: int
|
||||
accept_flag: int
|
||||
|
||||
class EventData(DtoModel):
|
||||
id: int
|
||||
active_event_id: int
|
||||
dialog_show_date: datetime
|
||||
show_start_dialog_flag: int
|
||||
show_progress_dialog_flag: int
|
||||
show_end_dialog_flag: int
|
||||
end_event_id: int
|
||||
|
||||
class RewardsData(DtoModel):
|
||||
rewards_id: int
|
||||
rewards_reason: str
|
||||
rewards: Reward
|
||||
|
||||
class LoginBonusData(DtoModel):
|
||||
gacha_id: int
|
||||
gacha_item_id: int
|
||||
category: int
|
||||
type: int
|
||||
|
||||
class FrozenData(DtoModel):
|
||||
frozen_status: int = 0
|
||||
|
||||
class PenaltyData(DtoModel):
|
||||
penalty_flag: int
|
||||
penalty_2_level: int
|
||||
|
||||
class AvatarGachaLottery(DtoModel):
|
||||
avatar_gacha_id: int
|
||||
|
||||
class StoryCourse(DtoModel):
|
||||
course_id: int
|
||||
count: int
|
||||
|
||||
class DriverDebut(DtoModel):
|
||||
play_count: int
|
||||
daily_play: int
|
||||
last_play_dt: datetime
|
||||
use_start_date: datetime
|
||||
use_end_date: datetime
|
||||
use_dt: datetime
|
||||
ticket_cnt: int
|
||||
ticket_get_bit: int
|
||||
|
||||
class TheoryData(DtoModel):
|
||||
play_count: int
|
||||
play_count_multi: int
|
||||
partner_id: int
|
||||
partner_progress: int
|
||||
partner_progress_score: int
|
||||
practice_start_rank: int
|
||||
general_flag: int
|
||||
vs_history: int
|
||||
vs_history_multi: int
|
||||
win_count: int
|
||||
win_count_multi: int
|
||||
|
||||
class TheoryCourse(DtoModel):
|
||||
course_id: int
|
||||
max_victory_grade: int
|
||||
run_count: int
|
||||
powerhouse_lv: int
|
||||
powerhouse_exp: int
|
||||
played_powerhouse_lv: int
|
||||
update_dt: datetime
|
||||
|
||||
class TheoryPartner(DtoModel):
|
||||
partner_id: int
|
||||
fellowship_lv: int
|
||||
fellowship_exp: int
|
||||
|
||||
class TheoryRunningParm(DtoModel):
|
||||
course_id: int
|
||||
attack: int
|
||||
defense: int
|
||||
safety: int
|
||||
runaway: int
|
||||
trick_flag: int
|
||||
|
||||
class SpecialModeHint(DtoModel):
|
||||
story_type: int
|
||||
hint_display_flag: int
|
||||
|
||||
user_base_data: Optional[UserBaseData] = None
|
||||
avatar_data: Optional[AvatarData] = None
|
||||
pick_up_car_data: Optional[list[CarData]] = []
|
||||
story_data: Optional[list[Story]] = []
|
||||
vsinfo_data: Optional[list[VsInfo]] = []
|
||||
stock_data: Optional[StockData] = None
|
||||
mission_data: Optional[MissionData] = None
|
||||
weekly_mission_data: Optional[list[WeeklyMission]] = None
|
||||
course_data: Optional[list[CourseData]] = []
|
||||
toppatu_event_data: Optional[ToppatuEventData] = None
|
||||
event_data: Optional[EventData] = None
|
||||
rewards_data: Optional[list[RewardsData]] = []
|
||||
login_bonus_data: Optional[LoginBonusData] = None
|
||||
frozen_data: Optional[FrozenData] = None
|
||||
penalty_data: Optional[PenaltyData] = None
|
||||
config_data: Optional[Season2ConfigData] = None
|
||||
battle_gift_data: Optional[list[BattleGift]] = []
|
||||
ticket_data: Optional[list[Ticket]] = []
|
||||
round_event: Optional[list[RoundEvent]] = []
|
||||
last_round_event: Optional[list[RoundEvent]] = []
|
||||
past_round_event: Optional[list[PastRoundEvent]] = []
|
||||
total_round_point: Optional[int] = None
|
||||
stamp_event_data: Optional[list[StampEventData]] = []
|
||||
avatar_gacha_lottery_data: Optional[list[AvatarGachaLottery]] = []
|
||||
fulltune_count: Optional[int] = None
|
||||
total_car_parts_count: Optional[int] = None
|
||||
car_layout_count: Optional[list[CarLayoutCount]] = []
|
||||
car_style_count: Optional[list[CarStyleCount]] = []
|
||||
car_use_count: Optional[list[CarUseCount]] = []
|
||||
maker_use_count: Optional[list[MakerUseCount]] = []
|
||||
story_course: Optional[list[StoryCourse]] = []
|
||||
driver_debut: Optional[DriverDebut] = None
|
||||
theory_data: Optional[TheoryData] = None
|
||||
theory_course_data: Optional[list[TheoryCourse]] = []
|
||||
theory_partner_data: Optional[list[TheoryPartner]] = []
|
||||
theory_running_pram_data: Optional[list[TheoryRunningParm]] = []
|
||||
special_mode_data: Optional[list[SpecialMode]] = []
|
||||
challenge_mode_data: Optional[list[ChallengeModeData]] = []
|
||||
season_rewards_data: Optional[list[RewardsData]] = []
|
||||
timetrial_event_data: Optional[TimetrialEventData2] = None
|
||||
special_mode_hint_data: Optional[SpecialModeHint] = None
|
||||
tips_info: Optional[Season2TipsInfo] = None
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateLogin(RequestGeneric):
|
||||
class RewardsId(BaseModel):
|
||||
rewards_id: int
|
||||
|
||||
mytitle_id: int
|
||||
mytitle_efffect_id: int
|
||||
sticker_id: int
|
||||
sticker_effect_id: int
|
||||
keyholder_id: int
|
||||
papercup_id: int
|
||||
tachometer_id: int
|
||||
aura_id: int
|
||||
aura_color_id: int
|
||||
aura_line_id: int
|
||||
bgm_id: int
|
||||
start_menu_bg_id: int
|
||||
cash: int
|
||||
total_cash: int
|
||||
dressup_point: int
|
||||
avatar_point: int
|
||||
stock_obj: StockData
|
||||
avatar_obj: AvatarData
|
||||
rewards_id_arr: list[RewardsId]
|
||||
ticket_data: list[Ticket]
|
||||
battle_gift_event_id: int
|
||||
gift_id: int
|
||||
season_rewards_id_arr: list[RewardsId]
|
||||
|
||||
|
||||
ResponseSeason2UserUpdateLogin = ResponseGeneric
|
||||
|
||||
|
||||
class ProfileResult(DtoModel):
|
||||
mileage: int
|
||||
stock_obj: StockData
|
||||
cash: int
|
||||
total_cash: int
|
||||
dressup_point: int
|
||||
avatar_point: int
|
||||
ticket_data: list[Ticket]
|
||||
reward_dist_obj: RewardDist
|
||||
aura_id: int
|
||||
aura_color_id: int
|
||||
aura_line_id: int
|
||||
driver_debut_obj: Optional[DriverDebut] = None
|
||||
mode_rank_obj: Optional[ModeRank] = None
|
||||
factory_disp: Optional[int] = None
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateTimeTrialResult(ProfileResult, RequestGeneric):
|
||||
goal_time: int
|
||||
section_time_1: int
|
||||
section_time_2: int
|
||||
section_time_3: int
|
||||
section_time_4: int
|
||||
style_car_id: int
|
||||
car_mileage: int
|
||||
course_id: int
|
||||
skill_level_exp: int
|
||||
mission: int
|
||||
mileage: int
|
||||
eval_id: int
|
||||
event_point: int
|
||||
|
||||
|
||||
class ResponseSeason2UserUpdateTimeTrialResult(ResponseGeneric):
|
||||
course_rank: int = 0
|
||||
course_car_rank: int = 0
|
||||
location_course_store_rank: int = 0
|
||||
car_use_count: list[CarUseCount] = []
|
||||
maker_use_count: list[MakerUseCount] = []
|
||||
timetrial_event_data: Optional[TimetrialEventData_2] = None
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateModeResult(RequestGeneric):
|
||||
mileage: int
|
||||
mytitle_id: int
|
||||
mytitle_efffect_id: int
|
||||
standby_play_flag: int
|
||||
mode_id: int
|
||||
tips_list: str
|
||||
config_obj: Season2ConfigData
|
||||
stock_obj: StockData
|
||||
aura_id: int
|
||||
aura_color_id: int
|
||||
aura_line_id: int
|
||||
cash: int
|
||||
total_cash: int
|
||||
dressup_point: int
|
||||
avatar_point: int
|
||||
ticket_data: list[Ticket]
|
||||
reward_dist_obj: RewardDist
|
||||
factory_disp: int
|
||||
|
||||
|
||||
class ResponseSeason2UserUpdateModeResult(ResponseGeneric):
|
||||
# 1 Success, 0 = Failed
|
||||
server_status: int = 1
|
||||
|
||||
|
||||
class CarTuneResult(DtoModel):
|
||||
style_car_id: int
|
||||
car_flag: int
|
||||
tune_point: int
|
||||
tune_level: int
|
||||
tune_parts: int
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateCarTune(RequestGeneric):
|
||||
style_car_id: int
|
||||
car_flag: int
|
||||
tune_point: int
|
||||
tune_level: int
|
||||
tune_parts: int
|
||||
use_ticket: int
|
||||
|
||||
|
||||
class ResponseSeason2UserUpdateCarTune(ResponseGeneric):
|
||||
story_data: list[Story]
|
||||
car_use_count: list[CarUseCount] = []
|
||||
maker_use_count: list[MakerUseCount] = []
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateStoryResult(ProfileResult, RequestGeneric):
|
||||
style_car_id: int
|
||||
car_mileage: int
|
||||
tune_point: int
|
||||
tune_level: int
|
||||
story_type: StoryTypeEnum
|
||||
chapter: int
|
||||
episode: int
|
||||
difficulty: int
|
||||
play_status: StoryPlayStatusEnum
|
||||
play_score: int
|
||||
chapter_clear: int
|
||||
chapter_loop_max: int
|
||||
course_id: int
|
||||
|
||||
|
||||
ResponseSeason2UserUpdateStoryResult = ResponseSeason2UserUpdateCarTune
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateSpecialResult(ProfileResult, RequestGeneric):
|
||||
style_car_id: int
|
||||
car_mileage: int
|
||||
story_type: StoryTypeEnum
|
||||
vs_type: int
|
||||
play_difficulty: int
|
||||
cleared_difficulty: int
|
||||
weak_difficulty: int
|
||||
eval_id: int
|
||||
advantage: int
|
||||
sec1_advantage_avg: int
|
||||
sec2_advantage_avg: int
|
||||
sec3_advantage_avg: int
|
||||
sec4_advantage_avg: int
|
||||
nearby_advantage_rate: int
|
||||
win_flag: int
|
||||
result: int
|
||||
record: int
|
||||
course_id: int
|
||||
last_play_course_id: int
|
||||
course_day: int
|
||||
hint_display_flag: int
|
||||
rental_flag: int = 0 # does not exist in season 2, but that makes it easer to code
|
||||
|
||||
|
||||
class ResponseSeason2UserUpdateSpecialResult(ResponseGeneric):
|
||||
special_mode_data: list[SpecialMode]
|
||||
car_use_count: list[CarUseCount]
|
||||
maker_use_count: list[MakerUseCount]
|
||||
|
||||
|
||||
class RequestSeason2UserGetPastSeasonRoundData(RequestGeneric):
|
||||
season_id: int
|
||||
|
||||
|
||||
class ResponseSeason2UserGetPastSeasonRoundData(ResponseGeneric):
|
||||
class PastSeasonRoundEventData(BaseModel):
|
||||
count: int
|
||||
win: int
|
||||
rank: int
|
||||
point: int
|
||||
total_round_point: int
|
||||
round_name: str
|
||||
round_id: int
|
||||
|
||||
season_id: int
|
||||
past_season_round_event_data: list[PastSeasonRoundEventData]
|
||||
|
||||
|
||||
class RequestSeason2UserGetTAData(RequestGeneric):
|
||||
pass
|
||||
|
||||
|
||||
class ResponseSeason2UserGetTAData(ResponseGeneric):
|
||||
timetrial_data: list[SeasonTimetrialData]
|
||||
|
||||
|
||||
RequestSeason2UserGetPastSeasonTAData = RequestSeason2UserGetPastSeasonRoundData
|
||||
|
||||
|
||||
class ResponseSeason2UserGetPastSeasonTAData(ResponseGeneric):
|
||||
season_id: int
|
||||
past_season_timetrial_data: list[SeasonTimetrialData]
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateTheoryResult(ProfileResult, RequestGeneric):
|
||||
result: int
|
||||
style_car_id: int
|
||||
car_mileage: int
|
||||
course_id: int
|
||||
course_day: int
|
||||
battle_mode: int
|
||||
vs_history: int
|
||||
vs_count: int
|
||||
advantage_1: int
|
||||
advantage_2: int
|
||||
advantage_3: int
|
||||
advantage_4: int
|
||||
rival_id_1: int
|
||||
rival_grade_1: int
|
||||
rival_style_car_id_1: int
|
||||
rival_powerhouse_lv_1: int
|
||||
rival_id_2: int
|
||||
rival_grade_2: int
|
||||
rival_style_car_id_2: int
|
||||
rival_powerhouse_lv_2: int
|
||||
rival_id_3: int
|
||||
rival_grade_3: int
|
||||
rival_style_car_id_3: int
|
||||
rival_powerhouse_lv_3: int
|
||||
powerhouse_lv: int
|
||||
powerhouse_exp: int
|
||||
partner_id: int
|
||||
partner_progress: int
|
||||
partner_progress_score: int
|
||||
practice_start_rank: int
|
||||
fellowship_lv: int
|
||||
fellowship_exp: int
|
||||
max_victory_grade: int
|
||||
general_flag: int
|
||||
tune_lv: int
|
||||
attack: int
|
||||
defense: int
|
||||
safety: int
|
||||
runaway: int
|
||||
trick_flag: int
|
||||
|
||||
|
||||
class ResponseSeason2UserUpdateTheoryResult(ResponseGeneric):
|
||||
played_powerhouse_lv: int
|
||||
car_use_count: list[CarUseCount]
|
||||
maker_use_count: list[MakerUseCount]
|
||||
play_count: int
|
||||
play_count_multi: int
|
||||
win_count: int
|
||||
win_count_multi: int
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateChallengeModeResult(ProfileResult, RequestGeneric):
|
||||
style_car_id: int
|
||||
car_mileage: int
|
||||
story_type: StoryTypeEnum
|
||||
vs_type: int
|
||||
play_difficulty: int
|
||||
cleared_difficulty: int
|
||||
eval_id: int
|
||||
advantage: int
|
||||
sec1_advantage_avg: int
|
||||
sec2_advantage_avg: int
|
||||
sec3_advantage_avg: int
|
||||
sec4_advantage_avg: int
|
||||
nearby_advantage_rate: int
|
||||
win_flag: int
|
||||
result: int
|
||||
record: int
|
||||
course_id: int
|
||||
last_play_course_id: int
|
||||
course_day: int
|
||||
|
||||
|
||||
class ResponseSeason2UserUpdateChallengeModeResult(ResponseGeneric):
|
||||
challenge_mode_data: list[ChallengeModeData]
|
||||
car_use_count: list[CarUseCount]
|
||||
maker_use_count: list[MakerUseCount]
|
||||
|
||||
|
||||
class BattleResult(DtoModel):
|
||||
result: int
|
||||
win_flg: int
|
||||
course_id: int
|
||||
vs_history: int
|
||||
break_count: int
|
||||
break_penalty_flag: bool
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateStoreBattleResult(
|
||||
BattleResult, ProfileResult, RequestGeneric
|
||||
):
|
||||
group_key: str
|
||||
style_car_id: int
|
||||
car_mileage: int
|
||||
course_day: int
|
||||
players_num: int
|
||||
winning: int
|
||||
advantage_1: int
|
||||
advantage_2: int
|
||||
advantage_3: int
|
||||
advantage_4: int
|
||||
select_course_id: int
|
||||
select_course_day: int
|
||||
select_course_random: int
|
||||
matching_success_sec: int
|
||||
boost_flag: int
|
||||
battle_gift_event_id: int
|
||||
gift_id: int
|
||||
|
||||
|
||||
class ResponseSeason2UserUpdateStoreBattleResult(ResponseGeneric):
|
||||
vsinfo_data: VsInfo
|
||||
car_use_count: list[CarUseCount]
|
||||
maker_use_count: list[MakerUseCount]
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateOnlineBattleResult(
|
||||
BattleResult, ProfileResult, RequestGeneric
|
||||
):
|
||||
group_key: str
|
||||
style_car_id: int
|
||||
car_mileage: int
|
||||
course_day: int
|
||||
players_num: int
|
||||
winning: int
|
||||
advantage_1: int
|
||||
advantage_2: int
|
||||
select_course_id: int
|
||||
select_course_day: int
|
||||
select_course_random: int
|
||||
matching_success_sec: int
|
||||
boost_flag: int
|
||||
battle_gift_event_id: int
|
||||
gift_id: int
|
||||
infinity_tune: int
|
||||
cpu_flag: int
|
||||
over_victory_flag: int
|
||||
round_point: int
|
||||
subcard_count: int
|
||||
course_select_priority: int
|
||||
|
||||
|
||||
class ResponseSeason2UserUpdateOnlineBattleResult(ResponseGeneric):
|
||||
vsinfo_data: VsInfo
|
||||
round_event: list[RoundEvent]
|
||||
car_use_count: list[CarUseCount]
|
||||
maker_use_count: list[MakerUseCount]
|
||||
|
||||
|
||||
RequestSeason2UserUpdateOnlineBattle = RequestGeneric
|
||||
|
||||
|
||||
class ResponseSeason2UserUpdateOnlineBattle(ResponseGeneric):
|
||||
bothwin_penalty: int
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateStampInfo(BaseModel):
|
||||
stamp_event_data: list[Season2StampEvent]
|
||||
|
||||
|
||||
ResponseSeason2UserUpdateStampInfo = ResponseGeneric
|
||||
@@ -0,0 +1,19 @@
|
||||
from titles.idac.model.common_model import DtoModel
|
||||
|
||||
|
||||
class VsInfoDto(DtoModel):
|
||||
battle_mode: int
|
||||
invalid: int
|
||||
str: int
|
||||
str_now: int
|
||||
lose_now: int
|
||||
vs_history: int
|
||||
break_count: int
|
||||
break_penalty_flag: bool
|
||||
|
||||
|
||||
class VsCourseInfoDto(DtoModel):
|
||||
battle_mode: int
|
||||
course_id: int
|
||||
vs_cnt: int
|
||||
vs_win: int
|
||||
@@ -0,0 +1,17 @@
|
||||
from titles.idac.model.common_model import *
|
||||
from titles.idac.model.season2.boot_model import ResponseSeason2BootGetConfigData
|
||||
|
||||
|
||||
class ResponseSeason3BootGetConfigData(ResponseSeason2BootGetConfigData):
|
||||
class LeagueRewards(BaseModel):
|
||||
league_rank_id: int
|
||||
target_type: int
|
||||
rank_start: int
|
||||
rank_end: int
|
||||
team_point: int
|
||||
reward_category: RewardCategoryEnum
|
||||
reward_type: int
|
||||
reward_reason: str
|
||||
|
||||
league_rewards: list[LeagueRewards] = []
|
||||
league_flag: int = 0 # 0 = open league?
|
||||
@@ -0,0 +1,127 @@
|
||||
from datetime import datetime
|
||||
from enum import IntEnum
|
||||
from typing import Optional
|
||||
|
||||
from titles.idac.const import LeagueRankEnum
|
||||
from titles.idac.model.common_model import AvatarData, DtoModel
|
||||
from titles.idac.model.season2.common_model import (
|
||||
RequestSeason2Headers,
|
||||
Season2ConfigData,
|
||||
Season2StampEvent,
|
||||
Season2TipsInfo,
|
||||
)
|
||||
|
||||
|
||||
class RequestSeason3Headers(RequestSeason2Headers):
|
||||
a_als: str = "1" # original ALLS check flag
|
||||
|
||||
|
||||
class RequestSeason3UserHeaders(RequestSeason3Headers):
|
||||
session: int
|
||||
session_procseq: int
|
||||
|
||||
|
||||
class Season3ConfigData(Season2ConfigData):
|
||||
favorite_bgm_list: Optional[str] = None
|
||||
break_flag: Optional[int] = None
|
||||
visible_my_stats: Optional[int] = None
|
||||
visible_rival_stats: Optional[int] = None
|
||||
|
||||
|
||||
class Season3TipsInfo(Season2TipsInfo):
|
||||
last_team_join_dt: Optional[datetime] = None
|
||||
|
||||
|
||||
class TeamRankingEnum(IntEnum):
|
||||
NONE = 0
|
||||
ACE = 1
|
||||
SECOND = 2
|
||||
THIRD = 3
|
||||
|
||||
|
||||
class TeamRoleEnum(IntEnum):
|
||||
MEMBER = 0
|
||||
LEADER = 1
|
||||
|
||||
|
||||
class TeamMemberActionEnum(IntEnum):
|
||||
NEW_TEAM_LEADER = 1
|
||||
JOIN_TEAM = 2
|
||||
LEAVE_TEAM = 3
|
||||
TEAM_POINTS_1000 = 4
|
||||
TEAM_POINTS_5000 = 5
|
||||
TEAM_POINTS_10000 = 6
|
||||
TEAM_POINTS_20000 = 7
|
||||
TEAM_POINTS_30000 = 8
|
||||
TEAM_POINTS_40000 = 9
|
||||
TEAM_POINTS_50000 = 10
|
||||
TEAM_POINTS_60000 = 11
|
||||
TEAM_POINTS_70000 = 12
|
||||
TEAM_POINTS_80000 = 13
|
||||
TEAM_POINTS_90000 = 14
|
||||
TEAM_POINTS_100000 = 15
|
||||
ACE = 16
|
||||
SECOND = 17
|
||||
THIRD = 18
|
||||
|
||||
|
||||
class PromoteEffectParam(DtoModel):
|
||||
next_league_rank_id: LeagueRankEnum = LeagueRankEnum.OPEN
|
||||
point: int = 0
|
||||
rank: int = 0
|
||||
demote_league_rank_id: LeagueRankEnum = LeagueRankEnum.OPEN
|
||||
demote_rank: int = 0
|
||||
|
||||
|
||||
class TeamInfo(DtoModel):
|
||||
class TeamInfoData(DtoModel):
|
||||
username: str
|
||||
action_id: TeamMemberActionEnum = TeamMemberActionEnum.JOIN_TEAM
|
||||
|
||||
team_id: int
|
||||
team_name: str
|
||||
role: TeamRoleEnum = TeamRoleEnum.MEMBER
|
||||
league_rank_id: LeagueRankEnum = LeagueRankEnum.OPEN
|
||||
team_sticker_id: int = 1
|
||||
daily_bonus: int = 0
|
||||
weekly_bonus: int = 0
|
||||
order: TeamRankingEnum = TeamRankingEnum.NONE
|
||||
country: int
|
||||
store_name: str
|
||||
team_info_list: list[TeamInfoData]
|
||||
|
||||
|
||||
class Top(DtoModel):
|
||||
id: int
|
||||
username: str
|
||||
avatar: AvatarData
|
||||
point: int
|
||||
order: TeamRankingEnum = TeamRankingEnum.NONE
|
||||
grade: int
|
||||
|
||||
class LeagueInfo(DtoModel):
|
||||
league_rank_id: LeagueRankEnum = LeagueRankEnum.OPEN
|
||||
rank: int = 0
|
||||
team_point: int = 0
|
||||
point: int = 0
|
||||
top_list: list[Top] = []
|
||||
|
||||
|
||||
# Shared properties for Season 3
|
||||
class Season3UpdateResultTeam(DtoModel):
|
||||
league_info: Optional[LeagueInfo] = None
|
||||
promote_effect_param: Optional[PromoteEffectParam] = None
|
||||
team_daily_bonus: int = 0
|
||||
team_weekly_bonus: int = 0
|
||||
|
||||
|
||||
# Shared properties for Season 3
|
||||
class Season3UpdateRequestTeam(DtoModel):
|
||||
league_point: int = 0
|
||||
team_mileage: int = 0
|
||||
team_daily_bonus: int = 0
|
||||
team_weekly_bonus: int = 0
|
||||
|
||||
|
||||
class Season3StampEvent(Season2StampEvent):
|
||||
convert_flag: bool = False
|
||||
@@ -0,0 +1,15 @@
|
||||
from titles.idac.model.common_model import *
|
||||
from titles.idac.model.season2.result_model import *
|
||||
from titles.idac.model.season3.common_model import *
|
||||
|
||||
|
||||
class RequestSeason3UserUpdateTimeTrialResult(
|
||||
RequestSeason2UserUpdateTimeTrialResult, Season3UpdateRequestTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ResponseSeason3UserUpdateTimeTrialResult(
|
||||
ResponseSeason2UserUpdateTimeTrialResult, Season3UpdateResultTeam
|
||||
):
|
||||
pass
|
||||
@@ -0,0 +1,242 @@
|
||||
from datetime import datetime
|
||||
from enum import IntEnum
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from titles.idac.model.common_model import *
|
||||
from titles.idac.model.season3.common_model import *
|
||||
|
||||
|
||||
class JoinStatusEnum(IntEnum):
|
||||
SUCCESS = 0
|
||||
INVALID_PASSWORD = 1
|
||||
TEAM_FULL = 2
|
||||
UNEXPECTED_ERROR = 3
|
||||
|
||||
|
||||
class TeamMemberCar(DtoModel):
|
||||
id: int
|
||||
styleCarId: int
|
||||
color: int
|
||||
bureau: int
|
||||
kana: int
|
||||
sNo: int
|
||||
lNo: int
|
||||
tuneLv: int
|
||||
carFlag: int
|
||||
tunePoint: int
|
||||
infinityTune: int
|
||||
tuneParts: int
|
||||
partsList: list[int]
|
||||
partsCount: int
|
||||
point: int
|
||||
|
||||
|
||||
class TeamMember(DtoModel):
|
||||
id: int
|
||||
username: str
|
||||
avatar: AvatarData
|
||||
grade: int
|
||||
role: TeamRoleEnum = TeamRoleEnum.MEMBER
|
||||
point: int = 0
|
||||
order: TeamRankingEnum = TeamRankingEnum.NONE
|
||||
last_play_dt: int
|
||||
|
||||
|
||||
class TeamInfoDto(DtoModel):
|
||||
id: Optional[int] = None
|
||||
version: int = 2
|
||||
name: str
|
||||
mileage: int = 0
|
||||
sticker_id: int = 1
|
||||
sticker_stock: str = "0111111111111".ljust(256, "0")
|
||||
country: int
|
||||
store_id: int
|
||||
password: Optional[str] = None
|
||||
auto_invite_flag: int
|
||||
|
||||
|
||||
class TeamMemberDto(DtoModel):
|
||||
team_id: int
|
||||
user: int
|
||||
version: int = 2
|
||||
role: TeamRoleEnum = TeamRoleEnum.MEMBER
|
||||
daily_bonus: int = 0
|
||||
weekly_bonus: int = 0
|
||||
create_date_daily: Optional[datetime] = Field(default_factory=datetime.now)
|
||||
create_date_weekly: Optional[datetime] = Field(default_factory=datetime.now)
|
||||
join_dt: Optional[datetime] = Field(default_factory=datetime.now)
|
||||
|
||||
|
||||
class TeamMemberWithRankingDto(TeamMemberDto):
|
||||
ranking: int = 0
|
||||
point: int = 0
|
||||
|
||||
|
||||
class TeamMemberInfoDto(DtoModel):
|
||||
team_id: int
|
||||
version: int = 2
|
||||
user: int
|
||||
action_id: TeamMemberActionEnum
|
||||
action_dt: datetime = Field(default_factory=datetime.now)
|
||||
|
||||
|
||||
class UserLeagueInfoDto(DtoModel):
|
||||
version: int = 2
|
||||
user: int
|
||||
team_id: Optional[int] = None
|
||||
round_id: int
|
||||
point: Optional[int] = None
|
||||
rewards_claimed: bool = False
|
||||
|
||||
|
||||
class LeagueInfoDto(DtoModel):
|
||||
version: int = 2
|
||||
round_id: int
|
||||
team_id: int
|
||||
league_rank_id: LeagueRankEnum = LeagueRankEnum.OPEN
|
||||
league_point: int = 0
|
||||
|
||||
|
||||
class LeagueInfoWithRankingDto(LeagueInfoDto):
|
||||
ranking: int = 0
|
||||
|
||||
|
||||
class RecommendTeamDto(DtoModel):
|
||||
team_id: int
|
||||
password: str
|
||||
team_name: str
|
||||
team_sticker_id: int
|
||||
country: int
|
||||
store_id: int
|
||||
league_rank_id: int
|
||||
team_point: int
|
||||
|
||||
|
||||
class RecommendTeam(DtoModel):
|
||||
class RecommendTeamMember(DtoModel):
|
||||
id: int
|
||||
username: str
|
||||
order: TeamRankingEnum
|
||||
role: TeamRoleEnum
|
||||
avatar: AvatarData
|
||||
|
||||
team_id: int
|
||||
search_id: int
|
||||
password: str
|
||||
team_name: str
|
||||
team_sticker_id: int
|
||||
country: int
|
||||
store_name: str
|
||||
league_rank_id: int
|
||||
team_point: int
|
||||
rank: int
|
||||
member: list[RecommendTeamMember]
|
||||
|
||||
|
||||
class RequestSeason3CreateTeam(RequestGeneric):
|
||||
team_name: str
|
||||
auto_invite_flag: int
|
||||
|
||||
|
||||
class ResponseSeason3CreateTeam(ResponseGeneric):
|
||||
team_info: TeamInfo
|
||||
league_info: LeagueInfo = LeagueInfo()
|
||||
auto_invite_flag: int = 0
|
||||
|
||||
|
||||
class ResponseSeason3LeftTeam(ResponseGeneric):
|
||||
left_status: int
|
||||
|
||||
|
||||
class RequestSeason3PreJoinTeam(RequestGeneric):
|
||||
password: str
|
||||
|
||||
|
||||
RequestSeason3JoinTeam = RequestSeason3PreJoinTeam
|
||||
|
||||
|
||||
class ResponseSeason3PreJoinTeam(ResponseGeneric):
|
||||
join_status: JoinStatusEnum = JoinStatusEnum.INVALID_PASSWORD
|
||||
team_info: Optional[TeamInfo] = None
|
||||
|
||||
|
||||
class ResponseSeason3JoinTeam(ResponseSeason3PreJoinTeam):
|
||||
league_info: LeagueInfo = LeagueInfo()
|
||||
team_member_cars: list[TeamMemberCar] = []
|
||||
|
||||
|
||||
class ResponseSeason3GetTeamManageData(ResponseGeneric):
|
||||
member_data: list[TeamMember]
|
||||
password: str
|
||||
auto_invite_flag: int
|
||||
|
||||
|
||||
class RequestSeason3TeamKickMember(RequestGeneric):
|
||||
id: int
|
||||
|
||||
|
||||
class ResponseSeason3TeamKickMember(ResponseGeneric):
|
||||
control_result: int
|
||||
team_point: int
|
||||
|
||||
|
||||
class RequestSeason3TeamChangeLeader(RequestGeneric):
|
||||
id: int
|
||||
|
||||
|
||||
class ResponsSeason3TeamChangeLeader(ResponseGeneric):
|
||||
control_result: int
|
||||
|
||||
|
||||
class ResponseSeason3TeamChangePassword(ResponseGeneric):
|
||||
control_result: int
|
||||
password: str
|
||||
|
||||
|
||||
class ResponseSeason3GetTeamInfoData(ResponseGeneric):
|
||||
class PastLeague(DtoModel):
|
||||
season_id: int
|
||||
round_event_id: int
|
||||
round_event_nm: str
|
||||
league_rank_id: int
|
||||
rank: int
|
||||
point: int
|
||||
top_list: list[Top]
|
||||
# not sure? does nothing
|
||||
reward_category: RewardCategoryEnum = RewardCategoryEnum.FT_TOKEN_PIECES
|
||||
reward_type: int = 1
|
||||
|
||||
member_data: list[TeamMember]
|
||||
past_league_data: list[PastLeague]
|
||||
|
||||
|
||||
class ResponseSeason3GetTeamHomeData(ResponseGeneric):
|
||||
team_member_cars: list[TeamMemberCar]
|
||||
|
||||
|
||||
class ResponseSeason3TeamGetTeamLogoStock(ResponseGeneric):
|
||||
sticker_stock: str
|
||||
|
||||
|
||||
class RequestSeason3TeamUpdateTeamLogoData(RequestGeneric):
|
||||
sticker_id: int
|
||||
|
||||
|
||||
class ResponseSeason3TeamUpdateTeamLogoData(ResponseGeneric):
|
||||
control_result: int
|
||||
sticker_id: int
|
||||
|
||||
|
||||
class ResponseSeason3TeamGetRecommendTeam(ResponseGeneric):
|
||||
join_status: int
|
||||
recommend_team_list: list[RecommendTeam]
|
||||
|
||||
|
||||
class RequestSeason3TeamUpdateTeamOption(RequestGeneric):
|
||||
auto_invite_flag: int
|
||||
|
||||
|
||||
class ResponseSeason3TeamUpdateTeamOption(ResponseGeneric):
|
||||
control_result: int
|
||||
auto_invite_flag: int
|
||||
@@ -0,0 +1,24 @@
|
||||
from typing import Optional
|
||||
|
||||
from titles.idac.model.common_model import DtoModel
|
||||
from titles.idac.model.season2.theory_model import (
|
||||
RequestSeason2TheoryMatching,
|
||||
ResponseSeason2TheoryMatching,
|
||||
Season2PowerMatch,
|
||||
)
|
||||
|
||||
RequestSeason3TheoryMatching = RequestSeason2TheoryMatching
|
||||
|
||||
|
||||
class Season3PowerMatch(Season2PowerMatch):
|
||||
stickerid: Optional[int] = None
|
||||
teamName: Optional[str] = None
|
||||
league_rank_id: Optional[int] = None
|
||||
|
||||
|
||||
class ResponseSeason3TheoryMatching(ResponseSeason2TheoryMatching):
|
||||
class RivalData(DtoModel):
|
||||
auto_match: list[Season3PowerMatch]
|
||||
power_match: list[Season3PowerMatch]
|
||||
|
||||
rival_data: RivalData
|
||||
@@ -0,0 +1,126 @@
|
||||
from typing import Optional
|
||||
|
||||
from titles.idac.model.common_model import Reward
|
||||
from titles.idac.model.season2.user_model import *
|
||||
from titles.idac.model.season3.common_model import *
|
||||
|
||||
|
||||
class RequestSeason3UserCreateAccount(RequestSeason2UserCreateAccount):
|
||||
config_obj: Season3ConfigData
|
||||
|
||||
|
||||
ResponseSeason3UserCreateAccount = ResponseSeason2UserCreateAccount
|
||||
|
||||
RequestSeason3UserGetData = RequestSeason2UserGetData
|
||||
|
||||
|
||||
class ResponseSeason3UserGetData(ResponseSeason2UserGetData):
|
||||
class RewardsData(BaseModel):
|
||||
rewards_id: int
|
||||
rewards_reason: str
|
||||
rewards: Reward
|
||||
|
||||
config_data: Optional[Season3ConfigData] = None
|
||||
tips_info: Optional[Season3TipsInfo] = None
|
||||
team_info: Optional[TeamInfo] = None
|
||||
league_info: Optional[LeagueInfo] = None
|
||||
last_league_info: list[LeagueInfo] = []
|
||||
league_rewards: list[RewardsData] = []
|
||||
team_invite_flag: int = 1
|
||||
|
||||
|
||||
class RequestSeason3UserUpdateLogin(RequestSeason2UserUpdateLogin):
|
||||
league_rewards_id_arr: list[RequestSeason2UserUpdateLogin.RewardsId]
|
||||
|
||||
|
||||
ResponseSeason3UserUpdateLogin = ResponseSeason2UserUpdateLogin
|
||||
|
||||
class RequestSeason3UserUpdateTimeTrialResult(
|
||||
RequestSeason2UserUpdateTimeTrialResult, Season3UpdateRequestTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ResponseSeason3UserUpdateTimeTrialResult(
|
||||
ResponseSeason2UserUpdateTimeTrialResult, Season3UpdateResultTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class RequestSeason3UserUpdateStoryResult(
|
||||
RequestSeason2UserUpdateStoryResult, Season3UpdateRequestTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ResponseSeason3UserUpdateStoryResult(
|
||||
ResponseSeason2UserUpdateStoryResult, Season3UpdateResultTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class RequestSeason3UserUpdateSpecialResult(
|
||||
RequestSeason2UserUpdateSpecialResult, Season3UpdateRequestTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ResponseSeason3UserUpdateSpecialResult(
|
||||
ResponseSeason2UserUpdateSpecialResult, Season3UpdateResultTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class RequestSeason3UserUpdateTheoryResult(
|
||||
RequestSeason2UserUpdateTheoryResult, Season3UpdateRequestTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ResponseSeason3UserUpdateTheoryResult(
|
||||
ResponseSeason2UserUpdateTheoryResult, Season3UpdateResultTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class RequestSeason3UserUpdateChallengeModeResult(
|
||||
RequestSeason2UserUpdateChallengeModeResult, Season3UpdateRequestTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ResponseSeason3UserUpdateChallengeModeResult(
|
||||
ResponseSeason2UserUpdateChallengeModeResult, Season3UpdateResultTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class RequestSeason3UserUpdateStoreBattleResult(
|
||||
RequestSeason2UserUpdateStoreBattleResult, Season3UpdateRequestTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ResponseSeason3UserUpdateStoreBattleResult(
|
||||
ResponseSeason2UserUpdateStoreBattleResult, Season3UpdateResultTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class RequestSeason3UserUpdateOnlineBattleResult(
|
||||
RequestSeason2UserUpdateOnlineBattleResult, Season3UpdateRequestTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class ResponseSeason3UserUpdateOnlineBattleResult(
|
||||
ResponseSeason2UserUpdateOnlineBattleResult, Season3UpdateResultTeam
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
class RequestSeason2UserUpdateStampInfo(RequestSeason2UserUpdateStampInfo):
|
||||
stamp_event_data: list[Season3StampEvent]
|
||||
|
||||
|
||||
ResponseSeason3UserUpdateStampInfo = RequestSeason2UserUpdateStampInfo
|
||||
+2
-2
@@ -8,8 +8,8 @@ from core.data import Data
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.const import IDACConstants
|
||||
from titles.idac.database import IDACData
|
||||
from titles.idac.schema.profile import *
|
||||
from titles.idac.schema.item import *
|
||||
from titles.idac.schema.profile_db import *
|
||||
from titles.idac.schema.item_db import *
|
||||
|
||||
|
||||
class IDACReader(BaseReader):
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
from typing import Optional, List, Dict
|
||||
from sqlalchemy import (
|
||||
and_,
|
||||
func,
|
||||
select,
|
||||
)
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from core.config import CoreConfig
|
||||
from core.data.schema import BaseData
|
||||
from titles.idac.const import StoryTypeEnum
|
||||
from titles.idac.mapper.challenge_mapper import IDACChallengeMapper
|
||||
from titles.idac.model.season2.challenge_model import *
|
||||
from titles.idac.schema.model.challenge import *
|
||||
|
||||
|
||||
class IDACChallengeDb(BaseData):
|
||||
def __init__(self, cfg: CoreConfig, conn: "sessionmaker[AsyncSession]"):
|
||||
super().__init__(cfg, conn)
|
||||
self.mapper = IDACChallengeMapper()
|
||||
|
||||
async def get_challenge(
|
||||
self, aime_id: int, vs_type: int, play_difficulty: int
|
||||
) -> Optional[ChallengeDto]:
|
||||
sql = select(ChallengeDb).where(
|
||||
and_(
|
||||
ChallengeDb.user == aime_id,
|
||||
ChallengeDb.vs_type == vs_type,
|
||||
ChallengeDb.play_difficulty == play_difficulty,
|
||||
)
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
challenge_db = result.scalars().first()
|
||||
|
||||
if challenge_db is None:
|
||||
return None
|
||||
return self.mapper.challenge_to_dto(challenge_db)
|
||||
|
||||
async def get_challenges(self, aime_id: int) -> Optional[List[ChallengeDto]]:
|
||||
sql = select(ChallengeDb).where(ChallengeDb.user == aime_id)
|
||||
result = await self.execute(sql)
|
||||
challenges_db = result.scalars().all()
|
||||
|
||||
if not challenges_db:
|
||||
return None
|
||||
return self.mapper.challenges_to_dto(challenges_db)
|
||||
|
||||
async def get_best_challenges_by_vs_type(
|
||||
self, aime_id: int, story_type: StoryTypeEnum = StoryTypeEnum.BUNTA
|
||||
) -> List[BestChallengeByVsTypeDto]:
|
||||
subquery = (
|
||||
select(
|
||||
ChallengeDb.story_type,
|
||||
ChallengeDb.user,
|
||||
ChallengeDb.vs_type,
|
||||
func.max(ChallengeDb.play_difficulty).label("last_play_lv"),
|
||||
)
|
||||
.where(
|
||||
and_(
|
||||
ChallengeDb.user == aime_id,
|
||||
ChallengeDb.story_type == story_type.value,
|
||||
)
|
||||
)
|
||||
.group_by(ChallengeDb.vs_type)
|
||||
.subquery()
|
||||
)
|
||||
|
||||
sql = (
|
||||
select(
|
||||
ChallengeDb.story_type,
|
||||
ChallengeDb.vs_type,
|
||||
ChallengeDb.cleared_difficulty.label("max_clear_lv"),
|
||||
ChallengeDb.play_difficulty.label("last_play_lv"),
|
||||
ChallengeDb.last_play_course_id,
|
||||
ChallengeDb.play_count,
|
||||
)
|
||||
.where(
|
||||
and_(
|
||||
ChallengeDb.user == subquery.c.user,
|
||||
ChallengeDb.vs_type == subquery.c.vs_type,
|
||||
ChallengeDb.play_difficulty == subquery.c.last_play_lv,
|
||||
),
|
||||
)
|
||||
.order_by(ChallengeDb.vs_type)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return []
|
||||
|
||||
rows = result.fetchall()
|
||||
if not rows:
|
||||
return []
|
||||
|
||||
return self.mapper.rows_to_best_challenge_dtos(rows)
|
||||
|
||||
async def put_challenge(
|
||||
self, aime_id: int, challenge_data: ChallengeDto
|
||||
) -> Optional[int]:
|
||||
challenge_data_db = challenge_data.model_dump_db()
|
||||
challenge_data_db["user"] = aime_id
|
||||
|
||||
sql = insert(ChallengeDb).values(**challenge_data_db)
|
||||
conflict = sql.on_duplicate_key_update(**challenge_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(f"put_challenge: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
@@ -1,63 +0,0 @@
|
||||
from datetime import datetime
|
||||
from typing import Dict, Optional
|
||||
from sqlalchemy import (
|
||||
Table,
|
||||
Column,
|
||||
UniqueConstraint,
|
||||
and_,
|
||||
)
|
||||
from sqlalchemy.types import Integer, TIMESTAMP
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
from core.data.schema import BaseData, metadata
|
||||
|
||||
lottery = Table(
|
||||
"idac_user_lottery",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("saved_value", Integer, nullable=False),
|
||||
Column("lottery_count", Integer, nullable=False),
|
||||
Column("create_date", TIMESTAMP, server_default=func.now()),
|
||||
UniqueConstraint("user", "version", name="idac_user_lottery_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
class IDACFactoryData(BaseData):
|
||||
async def get_lottery(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(lottery).where(
|
||||
and_(
|
||||
lottery.c.user == aime_id,
|
||||
lottery.c.version == version
|
||||
)
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def put_lottery(
|
||||
|
||||
self, aime_id: int, version: int, saved_value: int, lottery_count: int, create_date: datetime
|
||||
) -> Optional[int]:
|
||||
|
||||
lottery_data = {
|
||||
"user": aime_id,
|
||||
"version": version,
|
||||
"saved_value": saved_value,
|
||||
"lottery_count": lottery_count,
|
||||
"create_date": create_date
|
||||
}
|
||||
|
||||
sql = insert(lottery).values(**lottery_data)
|
||||
conflict = sql.on_duplicate_key_update(**lottery_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_lottery: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
@@ -0,0 +1,155 @@
|
||||
from typing import List, Optional
|
||||
from sqlalchemy import (
|
||||
and_,
|
||||
)
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from core.config import CoreConfig
|
||||
from core.data.schema import BaseData, metadata
|
||||
from titles.idac.mapper.factory_mapper import IDACFactoryMapper
|
||||
from titles.idac.model.season2.factory_model import CarDto, LotteryDto
|
||||
from titles.idac.schema.model.factory import *
|
||||
|
||||
|
||||
class IDACFactoryDb(BaseData):
|
||||
def __init__(self, cfg: CoreConfig, conn: "sessionmaker[AsyncSession]"):
|
||||
super().__init__(cfg, conn)
|
||||
self.mapper = IDACFactoryMapper()
|
||||
|
||||
async def get_lottery(self, aime_id: int, version: int) -> Optional[LotteryDto]:
|
||||
sql = select(LotteryDb).where(
|
||||
and_(LotteryDb.user == aime_id, LotteryDb.version == version)
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
lottery_db = result.scalars().first()
|
||||
|
||||
if lottery_db is None:
|
||||
return None
|
||||
return self.mapper.lottery_to_dto(lottery_db)
|
||||
|
||||
async def put_lottery(
|
||||
self,
|
||||
aime_id: int,
|
||||
version: int,
|
||||
lottery_data: LotteryDto,
|
||||
) -> Optional[int]:
|
||||
lottery_data_db = lottery_data.model_dump_db()
|
||||
lottery_data_db["user"] = aime_id
|
||||
lottery_data_db["version"] = version
|
||||
|
||||
sql = insert(LotteryDb).values(**lottery_data_db)
|
||||
conflict = sql.on_duplicate_key_update(**lottery_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(f"put_lottery: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def get_random_user_car(self, aime_id: int, version: int) -> Optional[CarDto]:
|
||||
sql = (
|
||||
select(CarDb)
|
||||
.where(and_(CarDb.user == aime_id, CarDb.version <= version))
|
||||
.order_by(func.rand())
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
car_db = result.scalars().first()
|
||||
|
||||
if car_db is None:
|
||||
return None
|
||||
return self.mapper.car_to_dto(car_db)
|
||||
|
||||
async def get_random_car(self, version: int) -> Optional[CarDto]:
|
||||
sql = (
|
||||
select(CarDb).where(CarDb.version <= version).order_by(func.rand()).limit(1)
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
car_db = result.scalars().first()
|
||||
|
||||
if car_db is None:
|
||||
return None
|
||||
return self.mapper.car_to_dto(car_db)
|
||||
|
||||
async def get_car(
|
||||
self, aime_id: int, version: int, style_car_id: int
|
||||
) -> Optional[CarDto]:
|
||||
sql = (
|
||||
select(CarDb)
|
||||
.where(
|
||||
and_(
|
||||
CarDb.user == aime_id,
|
||||
CarDb.version <= version,
|
||||
CarDb.style_car_id == style_car_id,
|
||||
)
|
||||
)
|
||||
.order_by(CarDb.version.desc())
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
car_db = result.scalars().first()
|
||||
|
||||
if car_db is None:
|
||||
return None
|
||||
return self.mapper.car_to_dto(car_db)
|
||||
|
||||
|
||||
async def get_cars(
|
||||
self, version: int, aime_id: int, only_pickup: bool = False
|
||||
) -> Optional[List[CarDto]]:
|
||||
# subquery to get the max version for each style_car_id
|
||||
max_version_subquery = (
|
||||
select(CarDb.style_car_id, func.max(CarDb.version).label("max_version"))
|
||||
.where(
|
||||
and_(
|
||||
CarDb.user == aime_id,
|
||||
CarDb.version <= version,
|
||||
)
|
||||
)
|
||||
.group_by(CarDb.style_car_id)
|
||||
.subquery()
|
||||
)
|
||||
|
||||
# main query joining with the subquery
|
||||
sql = (
|
||||
select(CarDb)
|
||||
.join(
|
||||
max_version_subquery,
|
||||
and_(
|
||||
CarDb.style_car_id == max_version_subquery.c.style_car_id,
|
||||
CarDb.version == max_version_subquery.c.max_version,
|
||||
),
|
||||
)
|
||||
.where(CarDb.user == aime_id)
|
||||
)
|
||||
|
||||
if only_pickup:
|
||||
sql = sql.where(CarDb.pickup_seq != 0)
|
||||
|
||||
sql = sql.order_by(CarDb.style_car_id)
|
||||
|
||||
result = await self.execute(sql)
|
||||
cars_db = result.scalars().all()
|
||||
|
||||
if not cars_db:
|
||||
return None
|
||||
return self.mapper.cars_to_dto_list(cars_db)
|
||||
|
||||
async def put_car(
|
||||
self, aime_id: int, version: int, car_data: CarDto
|
||||
) -> Optional[int]:
|
||||
car_data_db = car_data.model_dump_db()
|
||||
car_data_db["user"] = aime_id
|
||||
car_data_db["version"] = version
|
||||
|
||||
sql = insert(CarDb).values(**car_data_db)
|
||||
conflict = sql.on_duplicate_key_update(**car_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(f"put_car: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,147 @@
|
||||
from typing import Optional
|
||||
from sqlalchemy.sql import select
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from core.config import CoreConfig
|
||||
from core.data.schema.base import BaseData
|
||||
from titles.idac.mapper.item_mapper import IDACItemMapper
|
||||
from titles.idac.model.season2.item_model import *
|
||||
from titles.idac.schema.model.item import *
|
||||
|
||||
|
||||
class IDACItemDb(BaseData):
|
||||
def __init__(self, cfg: CoreConfig, conn: "sessionmaker[AsyncSession]"):
|
||||
super().__init__(cfg, conn)
|
||||
self.mapper = IDACItemMapper()
|
||||
|
||||
async def get_ticket(self, aime_id: int, ticket_id: int) -> Optional[TicketDto]:
|
||||
sql = select(TicketDb).where(
|
||||
TicketDb.user == aime_id, TicketDb.ticket_id == ticket_id
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
ticket_db = result.scalars().first()
|
||||
|
||||
if ticket_db is None:
|
||||
return None
|
||||
return self.mapper.ticket_to_dto(ticket_db)
|
||||
|
||||
async def get_tickets(self, aime_id: int) -> Optional[list[TicketDto]]:
|
||||
sql = select(TicketDb).where(TicketDb.user == aime_id)
|
||||
result = await self.execute(sql)
|
||||
tickets_db = result.scalars().all()
|
||||
|
||||
if not tickets_db:
|
||||
return None
|
||||
return self.mapper.tickets_to_dto(tickets_db)
|
||||
|
||||
async def get_stamps(self, aime_id: int) -> Optional[list[StampDto]]:
|
||||
sql = select(StampDb).where(StampDb.user == aime_id)
|
||||
result = await self.execute(sql)
|
||||
stamps_db = result.scalars().all()
|
||||
|
||||
if not stamps_db:
|
||||
return None
|
||||
return self.mapper.stamps_to_dto(stamps_db)
|
||||
|
||||
async def get_stamp(
|
||||
self, aime_id: int, m_stamp_event_id: int
|
||||
) -> Optional[StampDto]:
|
||||
sql = select(StampDb).where(
|
||||
StampDb.user == aime_id, StampDb.m_stamp_event_id == m_stamp_event_id
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
stamp_db = result.scalars().first()
|
||||
|
||||
if stamp_db is None:
|
||||
return None
|
||||
return self.mapper.stamp_to_dto(stamp_db)
|
||||
|
||||
async def get_timetrial_event(
|
||||
self, aime_id: int, timetrial_event_id: int
|
||||
) -> Optional[TimeTrialEventDto]:
|
||||
sql = select(TimeTrialEventDb).where(
|
||||
TimeTrialEventDb.user == aime_id,
|
||||
TimeTrialEventDb.timetrial_event_id == timetrial_event_id,
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
event_db = result.scalars().first()
|
||||
|
||||
if event_db is None:
|
||||
return None
|
||||
return self.mapper.timetrial_event_to_dto(event_db)
|
||||
|
||||
async def get_battle_gifts(
|
||||
self, aime_id: int, battle_gift_event_id: int
|
||||
) -> Optional[list[BattleGiftDto]]:
|
||||
sql = select(BattleGiftDb).where(
|
||||
BattleGiftDb.user == aime_id,
|
||||
BattleGiftDb.battle_gift_event_id == battle_gift_event_id,
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
gifts_db = result.scalars().all() if result else None
|
||||
|
||||
if not gifts_db:
|
||||
return None
|
||||
return self.mapper.battle_gifts_to_dto(gifts_db)
|
||||
|
||||
async def put_ticket(self, aime_id: int, ticket_data: TicketDto) -> Optional[int]:
|
||||
ticket_data_db = ticket_data.model_dump_db()
|
||||
ticket_data_db["user"] = aime_id
|
||||
|
||||
sql = insert(TicketDb).values(**ticket_data_db)
|
||||
conflict = sql.on_duplicate_key_update(**ticket_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(f"put_ticket: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_stamp(self, aime_id: int, stamp_data: StampDto) -> Optional[int]:
|
||||
stamp_data_db = stamp_data.model_dump_db()
|
||||
stamp_data_db["user"] = aime_id
|
||||
|
||||
sql = insert(StampDb).values(**stamp_data_db)
|
||||
conflict = sql.on_duplicate_key_update(**stamp_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(f"put_stamp: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_timetrial_event(
|
||||
self, aime_id: int, timetrial_event_data: TimeTrialEventDto
|
||||
) -> Optional[int]:
|
||||
timetrial_event_data_db = timetrial_event_data.model_dump_db()
|
||||
timetrial_event_data_db["user"] = aime_id
|
||||
|
||||
sql = insert(TimeTrialEventDb).values(**timetrial_event_data_db)
|
||||
conflict = sql.on_duplicate_key_update(**timetrial_event_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_timetrial_event: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_battle_gift(
|
||||
self, aime_id: int, battle_gift_data: BattleGiftDto
|
||||
) -> Optional[int]:
|
||||
battle_gift_data_db = battle_gift_data.model_dump_db()
|
||||
battle_gift_data_db["user"] = aime_id
|
||||
|
||||
sql = insert(BattleGiftDb).values(**battle_gift_data_db)
|
||||
conflict = sql.on_duplicate_key_update(**battle_gift_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_battle_gift: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
@@ -0,0 +1,41 @@
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
Integer,
|
||||
ForeignKey,
|
||||
UniqueConstraint,
|
||||
)
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
from core.data.schema import metadata
|
||||
|
||||
Base = declarative_base(metadata=metadata)
|
||||
|
||||
class ChallengeDb(Base):
|
||||
__tablename__ = "idac_user_challenge"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "vs_type", "play_difficulty", name="idac_user_challenge_uk"),
|
||||
{"mysql_charset": "utf8mb4"},
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"))
|
||||
vs_type = Column(Integer)
|
||||
play_difficulty = Column(Integer)
|
||||
cleared_difficulty = Column(Integer)
|
||||
story_type = Column(Integer)
|
||||
play_count = Column(Integer, server_default="1")
|
||||
weak_difficulty = Column(Integer, server_default="0")
|
||||
eval_id = Column(Integer)
|
||||
advantage = Column(Integer)
|
||||
sec1_advantage_avg = Column(Integer)
|
||||
sec2_advantage_avg = Column(Integer)
|
||||
sec3_advantage_avg = Column(Integer)
|
||||
sec4_advantage_avg = Column(Integer)
|
||||
nearby_advantage_rate = Column(Integer)
|
||||
win_flag = Column(Integer)
|
||||
result = Column(Integer)
|
||||
record = Column(Integer)
|
||||
course_id = Column(Integer)
|
||||
last_play_course_id = Column(Integer)
|
||||
style_car_id = Column(Integer)
|
||||
course_day = Column(Integer)
|
||||
@@ -0,0 +1,67 @@
|
||||
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
Integer,
|
||||
TIMESTAMP,
|
||||
ForeignKey,
|
||||
UniqueConstraint,
|
||||
String,
|
||||
JSON,
|
||||
func,
|
||||
)
|
||||
from core.data.schema.base import metadata
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
Base = declarative_base(metadata=metadata)
|
||||
|
||||
class LotteryDb(Base):
|
||||
__tablename__ = "idac_user_lottery"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "version", name="idac_user_lottery_uk"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"))
|
||||
version = Column(Integer, nullable=False)
|
||||
saved_value = Column(Integer, nullable=False)
|
||||
lottery_count = Column(Integer, nullable=False)
|
||||
create_date = Column(TIMESTAMP, server_default=func.now())
|
||||
|
||||
class CarDb(Base):
|
||||
__tablename__ = "idac_user_car"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "version", "style_car_id", name="idac_user_car_uk"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"))
|
||||
version = Column(Integer, nullable=False)
|
||||
car_id = Column(Integer)
|
||||
style_car_id = Column(Integer)
|
||||
color = Column(Integer)
|
||||
bureau = Column(Integer)
|
||||
kana = Column(Integer)
|
||||
s_no = Column(Integer)
|
||||
l_no = Column(Integer)
|
||||
car_flag = Column(Integer)
|
||||
tune_point = Column(Integer)
|
||||
tune_level = Column(Integer, server_default="1")
|
||||
tune_parts = Column(Integer)
|
||||
infinity_tune = Column(Integer, server_default="0")
|
||||
online_vs_win = Column(Integer, server_default="0")
|
||||
pickup_seq = Column(Integer, server_default="1")
|
||||
purchase_seq = Column(Integer, server_default="1")
|
||||
color_stock_list = Column(String(32))
|
||||
color_stock_new_list = Column(String(32))
|
||||
parts_stock_list = Column(String(48))
|
||||
parts_stock_new_list = Column(String(48))
|
||||
parts_set_equip_list = Column(String(48))
|
||||
parts_list = Column(JSON)
|
||||
equip_parts_count = Column(Integer, server_default="0")
|
||||
use_count = Column(Integer, server_default="0")
|
||||
story_use_count = Column(Integer, server_default="0")
|
||||
timetrial_use_count = Column(Integer, server_default="0")
|
||||
vs_use_count = Column(Integer, server_default="0")
|
||||
net_vs_use_count = Column(Integer, server_default="0")
|
||||
theory_use_count = Column(Integer, server_default="0")
|
||||
car_mileage = Column(Integer, server_default="0")
|
||||
@@ -0,0 +1,99 @@
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
Integer,
|
||||
ForeignKey,
|
||||
UniqueConstraint,
|
||||
func,
|
||||
TIMESTAMP,
|
||||
)
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
from core.data.schema.base import metadata
|
||||
|
||||
Base = declarative_base(metadata=metadata)
|
||||
|
||||
|
||||
class TicketDb(Base):
|
||||
__tablename__ = "idac_user_ticket"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "ticket_id", name="idac_user_ticket_uk"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
ticket_id = Column(Integer)
|
||||
ticket_cnt = Column(Integer)
|
||||
|
||||
|
||||
class StampDb(Base):
|
||||
__tablename__ = "idac_user_stamp"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "m_stamp_event_id", name="idac_user_stamp_uk"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer,
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
m_stamp_event_id = Column(Integer)
|
||||
select_flag = Column(Integer)
|
||||
stamp_masu = Column(Integer)
|
||||
daily_bonus = Column(Integer)
|
||||
weekly_bonus = Column(Integer)
|
||||
weekday_bonus = Column(Integer)
|
||||
weekend_bonus = Column(Integer)
|
||||
total_bonus = Column(Integer)
|
||||
day_total_bonus = Column(Integer)
|
||||
store_battle_bonus = Column(Integer)
|
||||
story_bonus = Column(Integer)
|
||||
online_battle_bonus = Column(Integer)
|
||||
timetrial_bonus = Column(Integer)
|
||||
fasteststreetlegaltheory_bonus = Column(Integer)
|
||||
collaboration_bonus = Column(Integer)
|
||||
add_bonus_daily_flag_1 = Column(Integer)
|
||||
add_bonus_daily_flag_2 = Column(Integer)
|
||||
add_bonus_daily_flag_3 = Column(Integer)
|
||||
create_date_daily = Column(TIMESTAMP, server_default=func.now())
|
||||
create_date_weekly = Column(TIMESTAMP, server_default=func.now())
|
||||
|
||||
|
||||
class TimeTrialEventDb(Base):
|
||||
__tablename__ = "idac_user_timetrial_event"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"user", "timetrial_event_id", name="idac_user_timetrial_event_uk"
|
||||
),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer,
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
timetrial_event_id = Column(Integer)
|
||||
point = Column(Integer)
|
||||
|
||||
|
||||
class BattleGiftDb(Base):
|
||||
__tablename__ = "idac_user_battle_gift"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"user", "battle_gift_event_id", "gift_id", name="idac_user_battle_gift_uk"
|
||||
),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer,
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
battle_gift_event_id = Column(Integer)
|
||||
gift_id = Column(Integer)
|
||||
gift_get_status = Column(Integer)
|
||||
received_date = Column(TIMESTAMP, server_default=func.now())
|
||||
@@ -0,0 +1,270 @@
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
Integer,
|
||||
String,
|
||||
ForeignKey,
|
||||
UniqueConstraint,
|
||||
func,
|
||||
TEXT,
|
||||
TIMESTAMP,
|
||||
)
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
from core.data.schema.base import metadata
|
||||
|
||||
Base = declarative_base(metadata=metadata)
|
||||
|
||||
|
||||
class ProfileDb(Base):
|
||||
__tablename__ = "idac_profile"
|
||||
__table_args__ = (UniqueConstraint("user", "version", name="idac_profile_uk"),)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer,
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
version = Column(Integer, nullable=False)
|
||||
username = Column(String(8))
|
||||
country = Column(Integer)
|
||||
store = Column(Integer)
|
||||
total_play = Column(Integer, server_default="0")
|
||||
daily_play = Column(Integer, server_default="0")
|
||||
day_play = Column(Integer, server_default="0")
|
||||
mileage = Column(Integer, server_default="0")
|
||||
device_version = Column(String(7), server_default="1.50.00")
|
||||
last_play_date = Column(TIMESTAMP, server_default=func.now())
|
||||
mytitle_id = Column(Integer, server_default="0")
|
||||
mytitle_efffect_id = Column(Integer, server_default="0")
|
||||
sticker_id = Column(Integer, server_default="0")
|
||||
sticker_effect_id = Column(Integer, server_default="0")
|
||||
papercup_id = Column(Integer, server_default="0")
|
||||
tachometer_id = Column(Integer, server_default="0")
|
||||
aura_id = Column(Integer, server_default="0")
|
||||
aura_color_id = Column(Integer, server_default="0")
|
||||
aura_line_id = Column(Integer, server_default="0")
|
||||
bgm_id = Column(Integer, server_default="0")
|
||||
keyholder_id = Column(Integer, server_default="0")
|
||||
start_menu_bg_id = Column(Integer, server_default="0")
|
||||
use_car_id = Column(Integer, server_default="1")
|
||||
use_style_car_id = Column(Integer, server_default="1")
|
||||
bothwin_count = Column(Integer, server_default="0")
|
||||
bothwin_score = Column(Integer, server_default="0")
|
||||
subcard_count = Column(Integer, server_default="0")
|
||||
vs_history = Column(Integer, server_default="0")
|
||||
stamp_key_assign_0 = Column(Integer)
|
||||
stamp_key_assign_1 = Column(Integer)
|
||||
stamp_key_assign_2 = Column(Integer)
|
||||
stamp_key_assign_3 = Column(Integer)
|
||||
name_change_category = Column(Integer, server_default="0")
|
||||
factory_disp = Column(Integer, server_default="0")
|
||||
create_date = Column(TIMESTAMP, server_default=func.now())
|
||||
cash = Column(Integer, server_default="0")
|
||||
dressup_point = Column(Integer, server_default="0")
|
||||
avatar_point = Column(Integer, server_default="0")
|
||||
total_cash = Column(Integer, server_default="0")
|
||||
total_car_parts_count = Column(Integer, server_default="0")
|
||||
|
||||
|
||||
class ProfileConfigDb(Base):
|
||||
__tablename__ = "idac_profile_config"
|
||||
__table_args__ = (UniqueConstraint("user", name="idac_profile_config_uk"),)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer,
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
steering_intensity = Column(Integer)
|
||||
transmission_type = Column(Integer)
|
||||
default_viewpoint = Column(Integer)
|
||||
favorite_bgm = Column(Integer)
|
||||
favorite_bgm_list = Column(TEXT)
|
||||
bgm_volume = Column(Integer)
|
||||
se_volume = Column(Integer)
|
||||
master_volume = Column(Integer)
|
||||
store_battle_policy = Column(Integer)
|
||||
battle_onomatope_display = Column(Integer)
|
||||
cornering_guide = Column(Integer)
|
||||
minimap = Column(Integer)
|
||||
line_guide = Column(Integer)
|
||||
ghost = Column(Integer)
|
||||
race_exit = Column(Integer)
|
||||
result_skip = Column(Integer)
|
||||
stamp_select_skip = Column(Integer)
|
||||
break_flag = Column(Integer)
|
||||
visible_my_stats = Column(Integer)
|
||||
visible_rival_stats = Column(Integer)
|
||||
|
||||
|
||||
class ProfileAvatarDb(Base):
|
||||
__tablename__ = "idac_profile_avatar"
|
||||
__table_args__ = (UniqueConstraint("user", name="idac_profile_avatar_uk"),)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer,
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
sex = Column(Integer)
|
||||
face = Column(Integer)
|
||||
eye = Column(Integer)
|
||||
mouth = Column(Integer)
|
||||
hair = Column(Integer)
|
||||
glasses = Column(Integer)
|
||||
face_accessory = Column(Integer)
|
||||
body = Column(Integer)
|
||||
body_accessory = Column(Integer)
|
||||
behind = Column(Integer)
|
||||
bg = Column(Integer)
|
||||
effect = Column(Integer)
|
||||
special = Column(Integer)
|
||||
|
||||
|
||||
class ProfileRankDb(Base):
|
||||
__tablename__ = "idac_profile_rank"
|
||||
__table_args__ = (UniqueConstraint("user", "version", name="idac_profile_rank_uk"),)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer,
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
version = Column(Integer, nullable=False)
|
||||
story_rank_exp = Column(Integer, server_default="0")
|
||||
story_rank = Column(Integer, server_default="1")
|
||||
time_trial_rank_exp = Column(Integer, server_default="0")
|
||||
time_trial_rank = Column(Integer, server_default="1")
|
||||
online_battle_rank_exp = Column(Integer, server_default="0")
|
||||
online_battle_rank = Column(Integer, server_default="1")
|
||||
store_battle_rank_exp = Column(Integer, server_default="0")
|
||||
store_battle_rank = Column(Integer, server_default="1")
|
||||
theory_exp = Column(Integer, server_default="0")
|
||||
theory_rank = Column(Integer, server_default="1")
|
||||
pride_group_id = Column(Integer, server_default="0")
|
||||
pride_point = Column(Integer, server_default="0")
|
||||
grade_exp = Column(Integer, server_default="0")
|
||||
grade = Column(Integer, server_default="1")
|
||||
grade_reward_dist = Column(Integer, server_default="0")
|
||||
story_rank_reward_dist = Column(Integer, server_default="0")
|
||||
time_trial_rank_reward_dist = Column(Integer, server_default="0")
|
||||
online_battle_rank_reward_dist = Column(Integer, server_default="0")
|
||||
store_battle_rank_reward_dist = Column(Integer, server_default="0")
|
||||
theory_rank_reward_dist = Column(Integer, server_default="0")
|
||||
max_attained_online_battle_rank = Column(Integer, server_default="0")
|
||||
max_attained_pride_point = Column(Integer, server_default="0")
|
||||
is_last_max = Column(Integer, server_default="0")
|
||||
|
||||
|
||||
class ProfileStockDb(Base):
|
||||
__tablename__ = "idac_profile_stock"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "version", name="idac_profile_stock_uk"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer,
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
version = Column(Integer, nullable=False)
|
||||
mytitle_list = Column(TEXT)
|
||||
mytitle_new_list = Column(TEXT)
|
||||
avatar_face_list = Column(TEXT)
|
||||
avatar_face_new_list = Column(TEXT)
|
||||
avatar_eye_list = Column(TEXT)
|
||||
avatar_eye_new_list = Column(TEXT)
|
||||
avatar_hair_list = Column(TEXT)
|
||||
avatar_hair_new_list = Column(TEXT)
|
||||
avatar_body_list = Column(TEXT)
|
||||
avatar_body_new_list = Column(TEXT)
|
||||
avatar_mouth_list = Column(TEXT)
|
||||
avatar_mouth_new_list = Column(TEXT)
|
||||
avatar_glasses_list = Column(TEXT)
|
||||
avatar_glasses_new_list = Column(TEXT)
|
||||
avatar_face_accessory_list = Column(TEXT)
|
||||
avatar_face_accessory_new_list = Column(TEXT)
|
||||
avatar_body_accessory_list = Column(TEXT)
|
||||
avatar_body_accessory_new_list = Column(TEXT)
|
||||
avatar_behind_list = Column(TEXT)
|
||||
avatar_behind_new_list = Column(TEXT)
|
||||
avatar_bg_list = Column(TEXT)
|
||||
avatar_bg_new_list = Column(TEXT)
|
||||
avatar_effect_list = Column(TEXT)
|
||||
avatar_effect_new_list = Column(TEXT)
|
||||
avatar_special_list = Column(TEXT)
|
||||
avatar_special_new_list = Column(TEXT)
|
||||
stamp_list = Column(TEXT)
|
||||
stamp_new_list = Column(TEXT)
|
||||
keyholder_list = Column(String(256))
|
||||
keyholder_new_list = Column(String(256))
|
||||
papercup_list = Column(TEXT)
|
||||
papercup_new_list = Column(TEXT)
|
||||
tachometer_list = Column(TEXT)
|
||||
tachometer_new_list = Column(TEXT)
|
||||
aura_list = Column(TEXT)
|
||||
aura_new_list = Column(TEXT)
|
||||
aura_color_list = Column(TEXT)
|
||||
aura_color_new_list = Column(TEXT)
|
||||
aura_line_list = Column(TEXT)
|
||||
aura_line_new_list = Column(TEXT)
|
||||
bgm_list = Column(TEXT)
|
||||
bgm_new_list = Column(TEXT)
|
||||
dx_color_list = Column(TEXT)
|
||||
dx_color_new_list = Column(TEXT)
|
||||
start_menu_bg_list = Column(TEXT)
|
||||
start_menu_bg_new_list = Column(TEXT)
|
||||
under_neon_list = Column(TEXT)
|
||||
|
||||
|
||||
class ProfileTheoryDb(Base):
|
||||
__tablename__ = "idac_profile_theory"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "version", name="idac_profile_theory_uk"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer,
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
version = Column(Integer, nullable=False)
|
||||
play_count = Column(Integer, server_default="0")
|
||||
play_count_multi = Column(Integer, server_default="0")
|
||||
partner_id = Column(Integer)
|
||||
partner_progress = Column(Integer)
|
||||
partner_progress_score = Column(Integer)
|
||||
practice_start_rank = Column(Integer, server_default="0")
|
||||
general_flag = Column(Integer, server_default="0")
|
||||
vs_history = Column(Integer, server_default="0")
|
||||
vs_history_multi = Column(Integer, server_default="0")
|
||||
win_count = Column(Integer, server_default="0")
|
||||
win_count_multi = Column(Integer, server_default="0")
|
||||
|
||||
|
||||
class ProfileTipsDb(Base):
|
||||
__tablename__ = "idac_profile_tips"
|
||||
__table_args__ = (UniqueConstraint("user", "version", name="idac_profile_tips_uk"),)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer,
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
)
|
||||
version = Column(Integer, nullable=False)
|
||||
tips_list = Column(String(16), server_default="QAAAAAAAAAAAAAAA")
|
||||
timetrial_play_count = Column(Integer, server_default="0")
|
||||
story_play_count = Column(Integer, server_default="0")
|
||||
store_battle_play_count = Column(Integer, server_default="0")
|
||||
online_battle_play_count = Column(Integer, server_default="0")
|
||||
special_play_count = Column(Integer, server_default="0")
|
||||
challenge_play_count = Column(Integer, server_default="0")
|
||||
theory_play_count = Column(Integer, server_default="0")
|
||||
last_team_join_dt = Column(TIMESTAMP)
|
||||
@@ -0,0 +1,50 @@
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
Integer,
|
||||
String,
|
||||
TIMESTAMP,
|
||||
ForeignKey,
|
||||
UniqueConstraint,
|
||||
func,
|
||||
)
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
from core.data.schema.base import metadata
|
||||
|
||||
Base = declarative_base(metadata=metadata)
|
||||
|
||||
|
||||
class RoundInfoDb(Base):
|
||||
__tablename__ = "idac_round_info"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("round_id_in_json", "season", name="idac_round_info_uk"),
|
||||
{"mysql_charset": "utf8mb4"},
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
round_id_in_json = Column(Integer, nullable=False)
|
||||
name = Column(String(64))
|
||||
season = Column(Integer, nullable=False)
|
||||
start_dt = Column(TIMESTAMP, server_default=func.now())
|
||||
end_dt = Column(TIMESTAMP, server_default=func.now())
|
||||
|
||||
|
||||
class UserRoundInfoDb(Base):
|
||||
__tablename__ = "idac_user_round_info"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "round_id", name="idac_user_round_info_uk"),
|
||||
{"mysql_charset": "utf8mb4"},
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
round_id = Column(
|
||||
Integer,
|
||||
ForeignKey("idac_round_info.id", ondelete="cascade", onupdate="cascade"),
|
||||
)
|
||||
count = Column(Integer)
|
||||
win = Column(Integer)
|
||||
point = Column(Integer)
|
||||
play_dt = Column(TIMESTAMP, server_default=func.now())
|
||||
@@ -0,0 +1,60 @@
|
||||
from sqlalchemy import Column, Integer, UniqueConstraint, ForeignKey
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
from core.data.schema import metadata
|
||||
|
||||
|
||||
Base = declarative_base(metadata=metadata)
|
||||
|
||||
|
||||
class StoryDb(Base):
|
||||
__tablename__ = "idac_user_story"
|
||||
__table_args__ = (UniqueConstraint("user", "chapter", name="idac_user_story_uk"),)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
story_type = Column(Integer)
|
||||
chapter = Column(Integer)
|
||||
loop_count = Column(Integer, server_default="1")
|
||||
|
||||
|
||||
class StoryEpisodeDb(Base):
|
||||
__tablename__ = "idac_user_story_episode"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"user", "chapter", "episode", name="idac_user_story_episode_uk"
|
||||
),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
chapter = Column(Integer)
|
||||
episode = Column(Integer)
|
||||
play_status = Column(Integer)
|
||||
|
||||
|
||||
class StoryEpisodeDifficultyDb(Base):
|
||||
__tablename__ = "idac_user_story_episode_difficulty"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"user",
|
||||
"episode",
|
||||
"difficulty",
|
||||
name="idac_user_story_episode_difficulty_uk",
|
||||
),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
episode = Column(Integer)
|
||||
difficulty = Column(Integer)
|
||||
play_count = Column(Integer)
|
||||
clear_count = Column(Integer)
|
||||
play_status = Column(Integer)
|
||||
play_score = Column(Integer)
|
||||
@@ -0,0 +1,118 @@
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
Integer,
|
||||
Boolean,
|
||||
String,
|
||||
ForeignKey,
|
||||
UniqueConstraint,
|
||||
func,
|
||||
TIMESTAMP,
|
||||
)
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
|
||||
from core.data.schema.base import metadata
|
||||
|
||||
Base = declarative_base(metadata=metadata)
|
||||
|
||||
|
||||
class TeamInfoDb(Base):
|
||||
__tablename__ = "idac_team_info"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("id", "version", name="idac_team_info_uk"),
|
||||
UniqueConstraint("password", name="idac_team_info_password_uk"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
version = Column(Integer, nullable=False, server_default="2")
|
||||
name = Column(String(15), nullable=False)
|
||||
mileage = Column(Integer, nullable=False, server_default="0")
|
||||
sticker_id = Column(Integer, nullable=False, server_default="1")
|
||||
sticker_stock = Column(String(256), server_default="0111111111111".ljust(256, "0"))
|
||||
country = Column(Integer, nullable=False)
|
||||
store_id = Column(Integer, nullable=False)
|
||||
password = Column(String(10), nullable=False)
|
||||
auto_invite_flag = Column(Integer, nullable=False, server_default="0")
|
||||
|
||||
|
||||
class TeamMemberDb(Base):
|
||||
__tablename__ = "idac_team_member"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("version", "team_id", "user", name="idac_team_member_uk"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
version = Column(Integer, nullable=False, server_default="2")
|
||||
team_id = Column(
|
||||
Integer,
|
||||
ForeignKey("idac_team_info.id", ondelete="cascade", onupdate="cascade"),
|
||||
)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
role = Column(Integer, nullable=False, server_default="0")
|
||||
daily_bonus = Column(Integer, nullable=False, server_default="0")
|
||||
weekly_bonus = Column(Integer, nullable=False, server_default="0")
|
||||
create_date_daily = Column(TIMESTAMP, nullable=False, server_default=func.now())
|
||||
create_date_weekly = Column(TIMESTAMP, nullable=False, server_default=func.now())
|
||||
join_dt = Column(TIMESTAMP, nullable=False, server_default=func.now())
|
||||
|
||||
|
||||
class TeamMemberInfoDb(Base):
|
||||
__tablename__ = "idac_team_member_info"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
version = Column(Integer, nullable=False, server_default="2")
|
||||
team_id = Column(
|
||||
Integer,
|
||||
ForeignKey("idac_team_info.id", ondelete="cascade", onupdate="cascade"),
|
||||
)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
action_id = Column(
|
||||
Integer,
|
||||
nullable=False,
|
||||
server_default="2",
|
||||
)
|
||||
action_dt = Column(TIMESTAMP, nullable=False, server_default=func.now())
|
||||
|
||||
|
||||
class LeagueInfoDb(Base):
|
||||
__tablename__ = "idac_league_info"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("version", "round_id", "team_id", name="idac_league_info_pk"),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
version = Column(Integer, nullable=False, server_default="2")
|
||||
round_id = Column(
|
||||
Integer,
|
||||
nullable=False,
|
||||
)
|
||||
team_id = Column(
|
||||
Integer,
|
||||
ForeignKey("idac_team_info.id", ondelete="cascade", onupdate="cascade"),
|
||||
)
|
||||
league_rank_id = Column(Integer, nullable=False, server_default="0")
|
||||
league_point = Column(Integer, nullable=False, server_default="0")
|
||||
|
||||
|
||||
class UserLeagueInfoDb(Base):
|
||||
__tablename__ = "idac_user_league_info"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"version", "user", "round_id", name="idac_user_league_info_pk"
|
||||
),
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
version = Column(Integer, nullable=False, server_default="2")
|
||||
user = Column(
|
||||
Integer,
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
)
|
||||
team_id = Column(Integer, nullable=False)
|
||||
round_id = Column(Integer, nullable=False)
|
||||
point = Column(Integer, nullable=False, server_default="0")
|
||||
rewards_claimed = Column(Boolean, nullable=False, server_default="0")
|
||||
@@ -0,0 +1,69 @@
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
Integer,
|
||||
TIMESTAMP,
|
||||
ForeignKey,
|
||||
UniqueConstraint,
|
||||
func,
|
||||
)
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
from core.data.schema import metadata
|
||||
|
||||
|
||||
Base = declarative_base(metadata=metadata)
|
||||
|
||||
|
||||
class TheoryCourseDb(Base):
|
||||
__tablename__ = "idac_user_theory_course"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "course_id", name="idac_user_theory_course_uk"),
|
||||
{"mysql_charset": "utf8mb4"},
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
course_id = Column(Integer)
|
||||
max_victory_grade = Column(Integer, server_default="0")
|
||||
run_count = Column(Integer, server_default="1")
|
||||
powerhouse_lv = Column(Integer)
|
||||
powerhouse_exp = Column(Integer)
|
||||
played_powerhouse_lv = Column(Integer)
|
||||
update_dt = Column(TIMESTAMP, server_default=func.now())
|
||||
|
||||
|
||||
class TheoryPartnerDb(Base):
|
||||
__tablename__ = "idac_user_theory_partner"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "partner_id", name="idac_user_theory_partner_uk"),
|
||||
{"mysql_charset": "utf8mb4"},
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
partner_id = Column(Integer)
|
||||
fellowship_lv = Column(Integer)
|
||||
fellowship_exp = Column(Integer)
|
||||
|
||||
|
||||
class TheoryRunningDb(Base):
|
||||
__tablename__ = "idac_user_theory_running"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "course_id", name="idac_user_theory_running_uk"),
|
||||
{"mysql_charset": "utf8mb4"},
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
course_id = Column(Integer)
|
||||
attack = Column(Integer)
|
||||
defense = Column(Integer)
|
||||
safety = Column(Integer)
|
||||
runaway = Column(Integer)
|
||||
trick_flag = Column(Integer)
|
||||
@@ -0,0 +1,67 @@
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
Integer,
|
||||
TIMESTAMP,
|
||||
UniqueConstraint,
|
||||
ForeignKey,
|
||||
Index,
|
||||
func,
|
||||
)
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
from core.data.schema import metadata
|
||||
|
||||
Base = declarative_base(metadata=metadata)
|
||||
|
||||
|
||||
class CourseDb(Base):
|
||||
__tablename__ = "idac_user_course"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "course_id", name="idac_user_course_uk"),
|
||||
{"mysql_charset": "utf8mb4"},
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
course_id = Column(Integer)
|
||||
run_counts = Column(Integer, server_default="1")
|
||||
skill_level_exp = Column(Integer, server_default="0")
|
||||
|
||||
|
||||
class TrialDb(Base):
|
||||
__tablename__ = "idac_user_time_trial"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"user",
|
||||
"version",
|
||||
"course_id",
|
||||
"style_car_id",
|
||||
name="idac_user_time_trial_uk",
|
||||
),
|
||||
Index(
|
||||
"idx_trial_ranking",
|
||||
"version",
|
||||
"course_id",
|
||||
"style_car_id",
|
||||
"user",
|
||||
"goal_time",
|
||||
),
|
||||
{"mysql_charset": "utf8mb4"},
|
||||
)
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
version = Column(Integer, nullable=False)
|
||||
style_car_id = Column(Integer)
|
||||
course_id = Column(Integer)
|
||||
eval_id = Column(Integer, server_default="0")
|
||||
goal_time = Column(Integer)
|
||||
section_time_1 = Column(Integer)
|
||||
section_time_2 = Column(Integer)
|
||||
section_time_3 = Column(Integer)
|
||||
section_time_4 = Column(Integer)
|
||||
mission = Column(Integer)
|
||||
play_dt = Column(TIMESTAMP, server_default=func.now())
|
||||
@@ -0,0 +1,47 @@
|
||||
from sqlalchemy import Column, UniqueConstraint, Integer, Boolean
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
from core.data.schema import metadata
|
||||
|
||||
Base = declarative_base(metadata=metadata)
|
||||
|
||||
|
||||
class VsInfoDb(Base):
|
||||
__tablename__ = "idac_user_vs_info"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user", "battle_mode", name="idac_user_vs_info_uk"),
|
||||
{"mysql_charset": "utf8mb4"},
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
battle_mode = Column(Integer)
|
||||
invalid = Column(Integer)
|
||||
str = Column(Integer)
|
||||
str_now = Column(Integer)
|
||||
lose_now = Column(Integer)
|
||||
vs_history = Column(Integer)
|
||||
break_count = Column(Integer)
|
||||
break_penalty_flag = Column(Boolean)
|
||||
|
||||
|
||||
class VsCourseInfoDb(Base):
|
||||
__tablename__ = "idac_user_vs_course_info"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"user", "battle_mode", "course_id", name="idac_user_vs_course_info_uk"
|
||||
),
|
||||
{"mysql_charset": "utf8mb4"},
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
user = Column(
|
||||
Integer, ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")
|
||||
)
|
||||
battle_mode = Column(Integer)
|
||||
course_id = Column(Integer)
|
||||
vs_cnt = Column(Integer)
|
||||
vs_win = Column(Integer)
|
||||
@@ -1,492 +0,0 @@
|
||||
from typing import Dict, List, Optional
|
||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger
|
||||
from sqlalchemy.engine.base import Connection
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
from core.data.schema import BaseData, metadata
|
||||
from core.config import CoreConfig
|
||||
|
||||
profile = Table(
|
||||
"idac_profile",
|
||||
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("username", String(8)),
|
||||
Column("country", Integer),
|
||||
Column("store", Integer),
|
||||
Column("team_id", Integer, server_default="0"),
|
||||
Column("total_play", Integer, server_default="0"),
|
||||
Column("daily_play", Integer, server_default="0"),
|
||||
Column("day_play", Integer, server_default="0"),
|
||||
Column("mileage", Integer, server_default="0"),
|
||||
Column("device_version", String(7), server_default="1.50.00"),
|
||||
Column("last_play_date", TIMESTAMP, server_default=func.now()),
|
||||
Column("mytitle_id", Integer, server_default="0"),
|
||||
Column("mytitle_efffect_id", Integer, server_default="0"),
|
||||
Column("sticker_id", Integer, server_default="0"),
|
||||
Column("sticker_effect_id", Integer, server_default="0"),
|
||||
Column("papercup_id", Integer, server_default="0"),
|
||||
Column("tachometer_id", Integer, server_default="0"),
|
||||
Column("aura_id", Integer, server_default="0"),
|
||||
Column("aura_color_id", Integer, server_default="0"),
|
||||
Column("aura_line_id", Integer, server_default="0"),
|
||||
Column("bgm_id", Integer, server_default="0"),
|
||||
Column("keyholder_id", Integer, server_default="0"),
|
||||
Column("start_menu_bg_id", Integer, server_default="0"),
|
||||
Column("use_car_id", Integer, server_default="1"),
|
||||
Column("use_style_car_id", Integer, server_default="1"),
|
||||
Column("bothwin_count", Integer, server_default="0"),
|
||||
Column("bothwin_score", Integer, server_default="0"),
|
||||
Column("subcard_count", Integer, server_default="0"),
|
||||
Column("vs_history", Integer, server_default="0"),
|
||||
Column("stamp_key_assign_0", Integer),
|
||||
Column("stamp_key_assign_1", Integer),
|
||||
Column("stamp_key_assign_2", Integer),
|
||||
Column("stamp_key_assign_3", Integer),
|
||||
Column("name_change_category", Integer, server_default="0"),
|
||||
Column("factory_disp", Integer, server_default="0"),
|
||||
Column("create_date", TIMESTAMP, server_default=func.now()),
|
||||
Column("cash", Integer, server_default="0"),
|
||||
Column("dressup_point", Integer, server_default="0"),
|
||||
Column("avatar_point", Integer, server_default="0"),
|
||||
Column("total_cash", Integer, server_default="0"),
|
||||
UniqueConstraint("user", "version", name="idac_profile_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
# No point setting defaults since the game sends everything on profile creation anyway
|
||||
config = Table(
|
||||
"idac_profile_config",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("config_id", Integer),
|
||||
Column("steering_intensity", Integer),
|
||||
Column("transmission_type", Integer),
|
||||
Column("default_viewpoint", Integer),
|
||||
Column("favorite_bgm", Integer),
|
||||
Column("bgm_volume", Integer),
|
||||
Column("se_volume", Integer),
|
||||
Column("master_volume", Integer),
|
||||
Column("store_battle_policy", Integer),
|
||||
Column("battle_onomatope_display", Integer),
|
||||
Column("cornering_guide", Integer),
|
||||
Column("minimap", Integer),
|
||||
Column("line_guide", Integer),
|
||||
Column("ghost", Integer),
|
||||
Column("race_exit", Integer),
|
||||
Column("result_skip", Integer),
|
||||
Column("stamp_select_skip", Integer),
|
||||
UniqueConstraint("user", name="idac_profile_config_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
# No point setting defaults since the game sends everything on profile creation anyway
|
||||
avatar = Table(
|
||||
"idac_profile_avatar",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column(
|
||||
"user",
|
||||
ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"),
|
||||
nullable=False,
|
||||
),
|
||||
Column("sex", Integer),
|
||||
Column("face", Integer),
|
||||
Column("eye", Integer),
|
||||
Column("mouth", Integer),
|
||||
Column("hair", Integer),
|
||||
Column("glasses", Integer),
|
||||
Column("face_accessory", Integer),
|
||||
Column("body", Integer),
|
||||
Column("body_accessory", Integer),
|
||||
Column("behind", Integer),
|
||||
Column("bg", Integer),
|
||||
Column("effect", Integer),
|
||||
Column("special", Integer),
|
||||
UniqueConstraint("user", name="idac_profile_avatar_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
rank = Table(
|
||||
"idac_profile_rank",
|
||||
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("story_rank_exp", Integer, server_default="0"),
|
||||
Column("story_rank", Integer, server_default="1"),
|
||||
Column("time_trial_rank_exp", Integer, server_default="0"),
|
||||
Column("time_trial_rank", Integer, server_default="1"),
|
||||
Column("online_battle_rank_exp", Integer, server_default="0"),
|
||||
Column("online_battle_rank", Integer, server_default="1"),
|
||||
Column("store_battle_rank_exp", Integer, server_default="0"),
|
||||
Column("store_battle_rank", Integer, server_default="1"),
|
||||
Column("theory_exp", Integer, server_default="0"),
|
||||
Column("theory_rank", Integer, server_default="1"),
|
||||
Column("pride_group_id", Integer, server_default="0"),
|
||||
Column("pride_point", Integer, server_default="0"),
|
||||
Column("grade_exp", Integer, server_default="0"),
|
||||
Column("grade", Integer, server_default="1"),
|
||||
Column("grade_reward_dist", Integer, server_default="0"),
|
||||
Column("story_rank_reward_dist", Integer, server_default="0"),
|
||||
Column("time_trial_rank_reward_dist", Integer, server_default="0"),
|
||||
Column("online_battle_rank_reward_dist", Integer, server_default="0"),
|
||||
Column("store_battle_rank_reward_dist", Integer, server_default="0"),
|
||||
Column("theory_rank_reward_dist", Integer, server_default="0"),
|
||||
Column("max_attained_online_battle_rank", Integer, server_default="1"),
|
||||
Column("max_attained_pride_point", Integer, server_default="0"),
|
||||
Column("is_last_max", Integer, server_default="0"),
|
||||
UniqueConstraint("user", "version", name="idac_profile_rank_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
stock = Table(
|
||||
"idac_profile_stock",
|
||||
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("mytitle_list", String(1024), server_default=""),
|
||||
Column("mytitle_new_list", String(1024), server_default=""),
|
||||
Column("avatar_face_list", String(255), server_default=""),
|
||||
Column("avatar_face_new_list", String(255), server_default=""),
|
||||
Column("avatar_eye_list", String(255), server_default=""),
|
||||
Column("avatar_eye_new_list", String(255), server_default=""),
|
||||
Column("avatar_hair_list", String(255), server_default=""),
|
||||
Column("avatar_hair_new_list", String(255), server_default=""),
|
||||
Column("avatar_body_list", String(255), server_default=""),
|
||||
Column("avatar_body_new_list", String(255), server_default=""),
|
||||
Column("avatar_mouth_list", String(255), server_default=""),
|
||||
Column("avatar_mouth_new_list", String(255), server_default=""),
|
||||
Column("avatar_glasses_list", String(255), server_default=""),
|
||||
Column("avatar_glasses_new_list", String(255), server_default=""),
|
||||
Column("avatar_face_accessory_list", String(255), server_default=""),
|
||||
Column("avatar_face_accessory_new_list", String(255), server_default=""),
|
||||
Column("avatar_body_accessory_list", String(255), server_default=""),
|
||||
Column("avatar_body_accessory_new_list", String(255), server_default=""),
|
||||
Column("avatar_behind_list", String(255), server_default=""),
|
||||
Column("avatar_behind_new_list", String(255), server_default=""),
|
||||
Column("avatar_bg_list", String(255), server_default=""),
|
||||
Column("avatar_bg_new_list", String(255), server_default=""),
|
||||
Column("avatar_effect_list", String(255), server_default=""),
|
||||
Column("avatar_effect_new_list", String(255), server_default=""),
|
||||
Column("avatar_special_list", String(255), server_default=""),
|
||||
Column("avatar_special_new_list", String(255), server_default=""),
|
||||
Column("stamp_list", String(255), server_default=""),
|
||||
Column("stamp_new_list", String(255), server_default=""),
|
||||
Column("keyholder_list", String(256), server_default=""),
|
||||
Column("keyholder_new_list", String(256), server_default=""),
|
||||
Column("papercup_list", String(255), server_default=""),
|
||||
Column("papercup_new_list", String(255), server_default=""),
|
||||
Column("tachometer_list", String(255), server_default=""),
|
||||
Column("tachometer_new_list", String(255), server_default=""),
|
||||
Column("aura_list", String(255), server_default=""),
|
||||
Column("aura_new_list", String(255), server_default=""),
|
||||
Column("aura_color_list", String(255), server_default=""),
|
||||
Column("aura_color_new_list", String(255), server_default=""),
|
||||
Column("aura_line_list", String(255), server_default=""),
|
||||
Column("aura_line_new_list", String(255), server_default=""),
|
||||
Column("bgm_list", String(255), server_default=""),
|
||||
Column("bgm_new_list", String(255), server_default=""),
|
||||
Column("dx_color_list", String(255), server_default=""),
|
||||
Column("dx_color_new_list", String(255), server_default=""),
|
||||
Column("start_menu_bg_list", String(255), server_default=""),
|
||||
Column("start_menu_bg_new_list", String(255), server_default=""),
|
||||
Column("under_neon_list", String(255), server_default=""),
|
||||
UniqueConstraint("user", "version", name="idac_profile_stock_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
theory = Table(
|
||||
"idac_profile_theory",
|
||||
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("play_count", Integer, server_default="0"),
|
||||
Column("play_count_multi", Integer, server_default="0"),
|
||||
Column("partner_id", Integer),
|
||||
Column("partner_progress", Integer),
|
||||
Column("partner_progress_score", Integer),
|
||||
Column("practice_start_rank", Integer, server_default="0"),
|
||||
Column("general_flag", Integer, server_default="0"),
|
||||
Column("vs_history", Integer, server_default="0"),
|
||||
Column("vs_history_multi", Integer, server_default="0"),
|
||||
Column("win_count", Integer, server_default="0"),
|
||||
Column("win_count_multi", Integer, server_default="0"),
|
||||
UniqueConstraint("user", "version", name="idac_profile_theory_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
tips = Table(
|
||||
"idac_profile_tips",
|
||||
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("tips_list", String(16), server_default="QAAAAAAAAAAAAAAA"),
|
||||
Column("timetrial_play_count", Integer, server_default="0"),
|
||||
Column("story_play_count", Integer, server_default="0"),
|
||||
Column("store_battle_play_count", Integer, server_default="0"),
|
||||
Column("online_battle_play_count", Integer, server_default="0"),
|
||||
Column("special_play_count", Integer, server_default="0"),
|
||||
Column("challenge_play_count", Integer, server_default="0"),
|
||||
Column("theory_play_count", Integer, server_default="0"),
|
||||
UniqueConstraint("user", "version", name="idac_profile_tips_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
class IDACProfileData(BaseData):
|
||||
def __init__(self, cfg: CoreConfig, conn: Connection) -> None:
|
||||
super().__init__(cfg, conn)
|
||||
self.date_time_format_ext = (
|
||||
"%Y-%m-%d %H:%M:%S.%f" # needs to be lopped off at [:-5]
|
||||
)
|
||||
self.date_time_format_short = "%Y-%m-%d"
|
||||
|
||||
async def get_profile(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(profile).where(
|
||||
and_(
|
||||
profile.c.user == aime_id,
|
||||
profile.c.version <= version,
|
||||
)
|
||||
).order_by(profile.c.version.desc())
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def get_different_random_profiles(
|
||||
self, aime_id: int, version: int, count: int = 9
|
||||
) -> Optional[Row]:
|
||||
sql = (
|
||||
select(profile)
|
||||
.where(
|
||||
and_(
|
||||
profile.c.user != aime_id,
|
||||
profile.c.version == version,
|
||||
)
|
||||
)
|
||||
.order_by(func.rand())
|
||||
.limit(count)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
async def get_profile_config(self, aime_id: int) -> Optional[Row]:
|
||||
sql = select(config).where(
|
||||
and_(
|
||||
config.c.user == aime_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def get_profile_avatar(self, aime_id: int) -> Optional[Row]:
|
||||
sql = select(avatar).where(
|
||||
and_(
|
||||
avatar.c.user == aime_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def get_profile_rank(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(rank).where(
|
||||
and_(
|
||||
rank.c.user == aime_id,
|
||||
rank.c.version <= version,
|
||||
)
|
||||
).order_by(rank.c.version.desc())
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def get_profile_stock(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(stock).where(
|
||||
and_(
|
||||
stock.c.user == aime_id,
|
||||
stock.c.version <= version,
|
||||
)
|
||||
).order_by(stock.c.version.desc())
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def get_profile_theory(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(theory).where(
|
||||
and_(
|
||||
theory.c.user == aime_id,
|
||||
theory.c.version <= version,
|
||||
)
|
||||
).order_by(theory.c.version.desc())
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def get_profile_tips(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(tips).where(
|
||||
and_(
|
||||
tips.c.user == aime_id,
|
||||
tips.c.version <= version,
|
||||
)
|
||||
).order_by(tips.c.version.desc())
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def put_profile(
|
||||
self, aime_id: int, version: int, profile_data: Dict
|
||||
) -> Optional[int]:
|
||||
profile_data["user"] = aime_id
|
||||
profile_data["version"] = version
|
||||
|
||||
sql = insert(profile).values(**profile_data)
|
||||
conflict = sql.on_duplicate_key_update(**profile_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(f"put_profile: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_config(self, aime_id: int, config_data: Dict) -> Optional[int]:
|
||||
config_data["user"] = aime_id
|
||||
|
||||
sql = insert(config).values(**config_data)
|
||||
conflict = sql.on_duplicate_key_update(**config_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_profile_config: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_avatar(self, aime_id: int, avatar_data: Dict) -> Optional[int]:
|
||||
avatar_data["user"] = aime_id
|
||||
|
||||
sql = insert(avatar).values(**avatar_data)
|
||||
conflict = sql.on_duplicate_key_update(**avatar_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_profile_avatar: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_rank(
|
||||
self, aime_id: int, version: int, rank_data: Dict
|
||||
) -> Optional[int]:
|
||||
rank_data["user"] = aime_id
|
||||
rank_data["version"] = version
|
||||
|
||||
sql = insert(rank).values(**rank_data)
|
||||
conflict = sql.on_duplicate_key_update(**rank_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(f"put_profile_rank: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_stock(
|
||||
self, aime_id: int, version: int, stock_data: Dict
|
||||
) -> Optional[int]:
|
||||
stock_data["user"] = aime_id
|
||||
stock_data["version"] = version
|
||||
|
||||
sql = insert(stock).values(**stock_data)
|
||||
conflict = sql.on_duplicate_key_update(**stock_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(f"put_profile_stock: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_theory(
|
||||
self, aime_id: int, version: int, theory_data: Dict
|
||||
) -> Optional[int]:
|
||||
theory_data["user"] = aime_id
|
||||
theory_data["version"] = version
|
||||
|
||||
sql = insert(theory).values(**theory_data)
|
||||
conflict = sql.on_duplicate_key_update(**theory_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_profile_theory: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_tips(
|
||||
self, aime_id: int, version: int, tips_data: Dict
|
||||
) -> Optional[int]:
|
||||
tips_data["user"] = aime_id
|
||||
tips_data["version"] = version
|
||||
|
||||
sql = insert(tips).values(**tips_data)
|
||||
conflict = sql.on_duplicate_key_update(**tips_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(
|
||||
f"put_profile_tips: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
@@ -0,0 +1,286 @@
|
||||
from typing import Optional
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
from sqlalchemy.orm import sessionmaker, aliased
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from core.data.schema import BaseData
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.model.season2.user_model import *
|
||||
from titles.idac.schema.model.profile import *
|
||||
from titles.idac.mapper.profile_mapper import IDACProfileMapper
|
||||
|
||||
|
||||
class IDACProfileDb(BaseData):
|
||||
def __init__(self, cfg: CoreConfig, conn: "sessionmaker[AsyncSession]") -> None:
|
||||
super().__init__(cfg, conn)
|
||||
|
||||
self.mapper = IDACProfileMapper()
|
||||
|
||||
self.date_time_format_ext = (
|
||||
"%Y-%m-%d %H:%M:%S.%f" # Needs to be lopped off at [:-5]
|
||||
)
|
||||
self.date_time_format_short = "%Y-%m-%d"
|
||||
|
||||
async def get_profile(self, aime_id: int, version: int) -> Optional[ProfileDto]:
|
||||
sql = (
|
||||
select([ProfileDb])
|
||||
.where(
|
||||
and_(
|
||||
ProfileDb.user == aime_id,
|
||||
ProfileDb.version <= version,
|
||||
)
|
||||
)
|
||||
.order_by(ProfileDb.version.desc())
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
profile_db = result.scalars().first()
|
||||
|
||||
if profile_db is None:
|
||||
return None
|
||||
return self.mapper.profile_to_dto(profile_db)
|
||||
|
||||
async def get_different_random_profiles(
|
||||
self, aime_id: int, version: int, count: int = 9
|
||||
) -> Optional[list[ProfileDto]]:
|
||||
sql = (
|
||||
select([ProfileDb])
|
||||
.where(
|
||||
and_(
|
||||
ProfileDb.user != aime_id,
|
||||
ProfileDb.version == version,
|
||||
)
|
||||
)
|
||||
.order_by(func.random())
|
||||
.limit(count)
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
profiles_db = result.scalars().all()
|
||||
|
||||
if not profiles_db:
|
||||
return None
|
||||
return [self.mapper.profile_to_dto(profile) for profile in profiles_db]
|
||||
|
||||
async def get_profile_config(self, aime_id: int) -> Optional[ProfileConfigDto]:
|
||||
sql = select(ProfileConfigDb).where(ProfileConfigDb.user == aime_id)
|
||||
result = await self.execute(sql)
|
||||
config_db = result.scalars().first()
|
||||
|
||||
if config_db is None:
|
||||
return None
|
||||
return self.mapper.profile_config_to_dto(config_db)
|
||||
|
||||
async def get_profile_avatar(self, aime_id: int) -> Optional[ProfileAvatarDto]:
|
||||
sql = select(ProfileAvatarDb).where(ProfileAvatarDb.user == aime_id)
|
||||
result = await self.execute(sql)
|
||||
avatar_db = result.scalars().first()
|
||||
|
||||
if avatar_db is None:
|
||||
return None
|
||||
return self.mapper.profile_avatar_to_dto(avatar_db)
|
||||
|
||||
async def get_profile_rank(
|
||||
self, aime_id: int, version: int
|
||||
) -> Optional[ProfileRankDto]:
|
||||
sql = (
|
||||
select(ProfileRankDb)
|
||||
.where(
|
||||
and_(
|
||||
ProfileRankDb.user == aime_id,
|
||||
ProfileRankDb.version <= version,
|
||||
)
|
||||
)
|
||||
.order_by(ProfileRankDb.version.desc())
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
rank_db = result.scalars().first()
|
||||
|
||||
if rank_db is None:
|
||||
return None
|
||||
return self.mapper.profile_rank_to_dto(rank_db)
|
||||
|
||||
async def get_profile_stock(
|
||||
self, aime_id: int, version: int
|
||||
) -> Optional[ProfileStockDto]:
|
||||
sql = (
|
||||
select(ProfileStockDb)
|
||||
.where(
|
||||
and_(
|
||||
ProfileStockDb.user == aime_id,
|
||||
ProfileStockDb.version <= version,
|
||||
)
|
||||
)
|
||||
.order_by(ProfileStockDb.version.desc())
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
stock_db = result.scalars().first()
|
||||
|
||||
if stock_db is None:
|
||||
return None
|
||||
return self.mapper.profile_stock_to_dto(stock_db)
|
||||
|
||||
async def get_profile_theory(
|
||||
self, aime_id: int, version: int
|
||||
) -> Optional[ProfileTheoryDto]:
|
||||
sql = (
|
||||
select(ProfileTheoryDb)
|
||||
.where(
|
||||
and_(
|
||||
ProfileTheoryDb.user == aime_id,
|
||||
ProfileTheoryDb.version <= version,
|
||||
)
|
||||
)
|
||||
.order_by(ProfileTheoryDb.version.desc())
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
theory_db = result.scalars().first()
|
||||
|
||||
if theory_db is None:
|
||||
return None
|
||||
return self.mapper.profile_theory_to_dto(theory_db)
|
||||
|
||||
async def get_profile_tips(
|
||||
self, aime_id: int, version: int
|
||||
) -> Optional[ProfileTipsDto]:
|
||||
sql = (
|
||||
select(ProfileTipsDb)
|
||||
.where(
|
||||
and_(
|
||||
ProfileTipsDb.user == aime_id,
|
||||
ProfileTipsDb.version <= version,
|
||||
)
|
||||
)
|
||||
.order_by(ProfileTipsDb.version.desc())
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
tips_db = result.scalars().first()
|
||||
|
||||
if tips_db is None:
|
||||
return None
|
||||
return self.mapper.profile_tips_to_dto(tips_db)
|
||||
|
||||
async def put_profile(
|
||||
self, aime_id: int, version: int, profile_data: ProfileDto
|
||||
) -> Optional[int]:
|
||||
profile_data_db = profile_data.model_dump_db()
|
||||
profile_data_db["user"] = aime_id
|
||||
profile_data_db["version"] = version
|
||||
|
||||
sql = insert(ProfileDb).values(profile_data_db)
|
||||
conflict = sql.on_duplicate_key_update(profile_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(f"put_profile: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_config(
|
||||
self, aime_id: int, config_data: ProfileConfigDto
|
||||
) -> Optional[int]:
|
||||
profile_config_data = config_data.model_dump_db(exclude={"id"})
|
||||
profile_config_data["user"] = aime_id
|
||||
|
||||
sql = insert(ProfileConfigDb).values(profile_config_data)
|
||||
conflict = sql.on_duplicate_key_update(profile_config_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_profile_config: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_avatar(
|
||||
self, aime_id: int, avatar_data: ProfileAvatarDto
|
||||
) -> Optional[int]:
|
||||
avatar_data_db = avatar_data.model_dump_db()
|
||||
avatar_data_db["user"] = aime_id
|
||||
|
||||
sql = insert(ProfileAvatarDb).values(avatar_data_db)
|
||||
conflict = sql.on_duplicate_key_update(avatar_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_profile_avatar: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_rank(
|
||||
self, aime_id: int, version: int, rank_data: ProfileRankDto
|
||||
) -> Optional[int]:
|
||||
rank_data_db = rank_data.model_dump_db()
|
||||
rank_data_db["user"] = aime_id
|
||||
rank_data_db["version"] = version
|
||||
|
||||
sql = insert(ProfileRankDb).values(rank_data_db)
|
||||
conflict = sql.on_duplicate_key_update(rank_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_profile_rank: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_stock(
|
||||
self, aime_id: int, version: int, stock_data: ProfileStockDto
|
||||
) -> Optional[int]:
|
||||
stock_data_db = stock_data.model_dump_db()
|
||||
# Also filter empty strings
|
||||
stock_data_db = {k: v for k, v in stock_data_db.items() if v != ""}
|
||||
stock_data_db["user"] = aime_id
|
||||
stock_data_db["version"] = version
|
||||
|
||||
sql = insert(ProfileStockDb).values(stock_data_db)
|
||||
conflict = sql.on_duplicate_key_update(stock_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_profile_stock: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_theory(
|
||||
self, aime_id: int, version: int, theory_data: ProfileTheoryDto
|
||||
) -> Optional[int]:
|
||||
theory_data_db = theory_data.model_dump_db()
|
||||
theory_data_db["user"] = aime_id
|
||||
theory_data_db["version"] = version
|
||||
|
||||
sql = insert(ProfileTheoryDb).values(theory_data_db)
|
||||
conflict = sql.on_duplicate_key_update(theory_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_profile_theory: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_profile_tips(
|
||||
self, aime_id: int, version: int, tips_data: ProfileTipsDto
|
||||
) -> Optional[int]:
|
||||
tips_data_db = tips_data.model_dump_db()
|
||||
tips_data_db["user"] = aime_id
|
||||
tips_data_db["version"] = version
|
||||
|
||||
sql = insert(ProfileTipsDb).values(tips_data_db)
|
||||
conflict = sql.on_duplicate_key_update(tips_data_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_profile_tips: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
@@ -0,0 +1,160 @@
|
||||
from typing import Optional
|
||||
from sqlalchemy import and_
|
||||
|
||||
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from core.data.schema import BaseData
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.mapper.round_mapper import IDACRoundMapper
|
||||
from titles.idac.model.season2.round_model import (
|
||||
RoundInfoDto,
|
||||
UserRoundInfoDto,
|
||||
UserRoundInfoWithRankDto,
|
||||
)
|
||||
from titles.idac.schema.model.round import RoundInfoDb, UserRoundInfoDb
|
||||
|
||||
|
||||
class IDACRoundDb(BaseData):
|
||||
def __init__(self, cfg: CoreConfig, conn: "sessionmaker[AsyncSession]"):
|
||||
super().__init__(cfg, conn)
|
||||
|
||||
self.mapper = IDACRoundMapper()
|
||||
|
||||
async def get_round_info(
|
||||
self, version: int, round_id: int
|
||||
) -> Optional[RoundInfoDto]:
|
||||
sql = select(RoundInfoDb).where(
|
||||
and_(
|
||||
RoundInfoDb.season == version, RoundInfoDb.round_id_in_json == round_id
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
round_info_db = result.scalars().one_or_none()
|
||||
|
||||
if round_info_db is None:
|
||||
return None
|
||||
|
||||
return self.mapper.round_info_to_dto(round_info_db)
|
||||
|
||||
async def get_all_previous_round_info(
|
||||
self, season: int, current_round_id: int
|
||||
) -> list[RoundInfoDto]:
|
||||
sql = (
|
||||
select(RoundInfoDb)
|
||||
.where(
|
||||
and_(
|
||||
RoundInfoDb.season == season,
|
||||
RoundInfoDb.round_id_in_json < current_round_id,
|
||||
)
|
||||
)
|
||||
.order_by(RoundInfoDb.round_id_in_json.asc())
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
round_info_db = result.scalars().all()
|
||||
|
||||
return self.mapper.round_info_list_to_dto_list(round_info_db)
|
||||
|
||||
async def put_round_info(self, version: int, round: RoundInfoDto) -> Optional[int]:
|
||||
round_info_db = round.model_dump_db()
|
||||
round_info_db["season"] = version
|
||||
|
||||
sql = insert(RoundInfoDb).values(round_info_db)
|
||||
conflict = sql.on_duplicate_key_update(round_info_db)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_round_info: Failed to update! round_id: {round.round_id_in_json}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def get_round_rank_by_id(
|
||||
self, aime_id: int, round_event_id: int
|
||||
) -> Optional[UserRoundInfoWithRankDto]:
|
||||
ranking_stmt = (
|
||||
select(
|
||||
UserRoundInfoDb,
|
||||
func.rank()
|
||||
.over(order_by=[UserRoundInfoDb.point.desc(), UserRoundInfoDb.play_dt])
|
||||
.label("rank"),
|
||||
)
|
||||
.where(UserRoundInfoDb.round_id == round_event_id)
|
||||
.subquery()
|
||||
)
|
||||
|
||||
sql = select(ranking_stmt).where(ranking_stmt.c.user == aime_id)
|
||||
|
||||
result = await self.execute(sql)
|
||||
user_round_with_rank = result.fetchone()
|
||||
|
||||
if not user_round_with_rank:
|
||||
return None
|
||||
|
||||
return self.mapper.user_round_info_with_rank_to_dto(user_round_with_rank)
|
||||
|
||||
async def get_round_top_five(
|
||||
self, round_event_id: int
|
||||
) -> list[UserRoundInfoWithRankDto]:
|
||||
sql = (
|
||||
select(
|
||||
UserRoundInfoDb,
|
||||
func.rank()
|
||||
.over(order_by=[UserRoundInfoDb.point.desc(), UserRoundInfoDb.play_dt])
|
||||
.label("rank"),
|
||||
)
|
||||
.where(UserRoundInfoDb.round_id == round_event_id)
|
||||
.order_by(UserRoundInfoDb.point.desc(), UserRoundInfoDb.play_dt)
|
||||
.limit(5)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
user_round_with_rank_list = result.fetchall()
|
||||
|
||||
return self.mapper.user_round_info_with_rank_list_to_dto_list(
|
||||
user_round_with_rank_list
|
||||
)
|
||||
|
||||
async def get_user_round_info(
|
||||
self, aime_id: int, round_id: int
|
||||
) -> Optional[UserRoundInfoDto]:
|
||||
sql = select(UserRoundInfoDb).where(
|
||||
and_(
|
||||
UserRoundInfoDb.user == aime_id,
|
||||
UserRoundInfoDb.round_id == round_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
user_round_info = result.scalars().one_or_none()
|
||||
|
||||
if user_round_info is None:
|
||||
return None
|
||||
|
||||
return self.mapper.user_round_info_to_dto(user_round_info)
|
||||
|
||||
async def put_user_round_info(
|
||||
self, aime_id: int, round_id: int, round_data: UserRoundInfoDto
|
||||
) -> Optional[int]:
|
||||
round_user_db = round_data.model_dump_db()
|
||||
round_user_db["user"] = aime_id
|
||||
round_user_db["round_id"] = round_id
|
||||
|
||||
sql = insert(UserRoundInfoDb).values(round_data)
|
||||
conflict = sql.on_duplicate_key_update(round_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_round_user_info: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
# TODO: get top five players of last round event for Boot/GetConfigData
|
||||
@@ -1,145 +0,0 @@
|
||||
from typing import Dict, List, Optional
|
||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_, update
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger
|
||||
from sqlalchemy.engine.base import Connection
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
from datetime import datetime
|
||||
|
||||
from core.data.schema import BaseData, metadata
|
||||
from core.config import CoreConfig
|
||||
|
||||
round_details = Table(
|
||||
"idac_round_info",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("round_id_in_json", Integer),
|
||||
Column("name", String(64)),
|
||||
Column("season", Integer),
|
||||
Column("start_dt", TIMESTAMP, server_default=func.now()),
|
||||
Column("end_dt", TIMESTAMP, server_default=func.now()),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
round_info = Table(
|
||||
"idac_user_round_info",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("round_id", ForeignKey("idac_round_info.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("count", Integer),
|
||||
Column("win", Integer),
|
||||
Column("point", Integer),
|
||||
Column("play_dt", TIMESTAMP, server_default=func.now()),
|
||||
UniqueConstraint("user", "round_id", name="idac_user_round_info_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
class IDACOnlineRounds(BaseData):
|
||||
# get player's ranking from a specified round event
|
||||
async def get_round_rank_by_id(self, aime_id: int, round_event_id: int) -> Optional[Row]:
|
||||
subquery = (
|
||||
select([func.group_concat(func.concat(round_info.c.user, '|', round_info.c.point),order_by=[round_info.c.point.desc(), round_info.c.play_dt])])
|
||||
.select_from(round_info)
|
||||
.where(round_info.c.round_id == round_event_id)
|
||||
.as_scalar()
|
||||
)
|
||||
|
||||
sql = (
|
||||
select([func.find_in_set(func.concat(round_info.c.user, '|', round_info.c.point), subquery.label('rank'))])
|
||||
.select_from(round_info)
|
||||
.where(
|
||||
and_(
|
||||
round_info.c.user == aime_id,
|
||||
round_info.c.round_id == round_event_id
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
|
||||
# get player's info from a specified round event
|
||||
async def get_round_info_by_id(self, aime_id: int, round_event_id: int) -> Optional[Row]:
|
||||
sql = select(round_info).where(
|
||||
and_(
|
||||
round_info.c.user == aime_id,
|
||||
round_info.c.round_id == round_event_id
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
# get top 5 of a specified round event
|
||||
async def get_round_top_five(self, round_event_id: int) -> Optional[Row]:
|
||||
subquery = (
|
||||
select([func.group_concat(func.concat(round_info.c.user, '|', round_info.c.point),order_by=[round_info.c.point.desc(), round_info.c.play_dt])])
|
||||
.select_from(round_info)
|
||||
.where(round_info.c.round_id == round_event_id)
|
||||
.as_scalar()
|
||||
)
|
||||
|
||||
sql = (
|
||||
select([func.find_in_set(func.concat(round_info.c.user, '|', round_info.c.point), subquery.label('rank'))], round_info.c.user)
|
||||
.select_from(round_info)
|
||||
.where(round_info.c.round_id == round_event_id)
|
||||
.limit(5)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
# save players info to a specified round event
|
||||
async def put_round_event(
|
||||
self, aime_id: int, round_id: int, round_data: Dict
|
||||
) -> Optional[int]:
|
||||
round_data["user"] = aime_id
|
||||
round_data["round_id"] = round_id
|
||||
|
||||
sql = insert(round_info).values(**round_data)
|
||||
conflict = sql.on_duplicate_key_update(**round_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"putround: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
# insert if the event does not exist in database
|
||||
async def _try_load_round_event(
|
||||
self, round_id: int, version: int, round_data: Dict
|
||||
) -> Optional[int]:
|
||||
|
||||
sql = select(round_details).where(round_details.c.round_id_in_json == round_id)
|
||||
result = await self.execute(sql)
|
||||
|
||||
# check if the round already exists
|
||||
existing_round = result.fetchone() if result else None
|
||||
|
||||
if existing_round is None:
|
||||
tmp = {
|
||||
"round_id_in_json": round_id,
|
||||
"name": round_data["round_event_nm"],
|
||||
"season": version,
|
||||
"start_dt": datetime.fromtimestamp(round_data["start_dt"]),
|
||||
"end_dt": datetime.fromtimestamp(round_data["end_dt"]),
|
||||
}
|
||||
|
||||
sql = insert(round_details).values(**tmp)
|
||||
result = await self.execute(sql)
|
||||
|
||||
return result.lastrowid
|
||||
|
||||
return existing_round["id"]
|
||||
|
||||
# TODO: get top five players of last round event for Boot/GetConfigData
|
||||
@@ -0,0 +1,153 @@
|
||||
from typing import Optional, List
|
||||
from sqlalchemy import and_, update, select
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from core.config import CoreConfig
|
||||
from core.data.schema import BaseData
|
||||
from titles.idac.mapper.story_mapper import IDACStoryMapper
|
||||
from titles.idac.model.season2.story_model import *
|
||||
from .model.story import *
|
||||
|
||||
|
||||
class IDACStoryDb(BaseData):
|
||||
def __init__(self, cfg: CoreConfig, conn: "sessionmaker[AsyncSession]"):
|
||||
super().__init__(cfg, conn)
|
||||
self.mapper = IDACStoryMapper()
|
||||
|
||||
async def get_story(self, aime_id: int, chapter_id: int) -> Optional[StoryDto]:
|
||||
sql = select(StoryDb).where(
|
||||
and_(StoryDb.user == aime_id, StoryDb.chapter == chapter_id)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
story_db = result.scalars().first()
|
||||
|
||||
if story_db is None:
|
||||
return None
|
||||
return self.mapper.story_to_dto(story_db)
|
||||
|
||||
async def get_stories(self, aime_id: int) -> Optional[List[StoryDto]]:
|
||||
sql = select(StoryDb).where(StoryDb.user == aime_id)
|
||||
result = await self.execute(sql)
|
||||
stories_db = result.scalars().all()
|
||||
|
||||
if not stories_db:
|
||||
return None
|
||||
return self.mapper.stories_to_dto(stories_db)
|
||||
|
||||
async def get_story_episodes(
|
||||
self, aime_id: int, chapter_id: int
|
||||
) -> Optional[List[StoryEpisodeDto]]:
|
||||
sql = select(StoryEpisodeDb).where(
|
||||
and_(StoryEpisodeDb.user == aime_id, StoryEpisodeDb.chapter == chapter_id)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
episodes_db = result.scalars().all()
|
||||
|
||||
if not episodes_db:
|
||||
return None
|
||||
return self.mapper.story_episodes_to_dto(episodes_db)
|
||||
|
||||
async def get_story_episode(
|
||||
self, aime_id: int, episode_id: int
|
||||
) -> Optional[StoryEpisodeDto]:
|
||||
sql = select(StoryEpisodeDb).where(
|
||||
and_(StoryEpisodeDb.user == aime_id, StoryEpisodeDb.episode == episode_id)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
episode_db = result.scalars().first()
|
||||
|
||||
if episode_db is None:
|
||||
return None
|
||||
return self.mapper.story_episode_to_dto(episode_db)
|
||||
|
||||
async def get_story_episode_difficulties(
|
||||
self, aime_id: int, episode_id: int
|
||||
) -> Optional[List[StoryEpisodeDifficultyDto]]:
|
||||
sql = select(StoryEpisodeDifficultyDb).where(
|
||||
and_(
|
||||
StoryEpisodeDifficultyDb.user == aime_id,
|
||||
StoryEpisodeDifficultyDb.episode == episode_id,
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
difficulties_db = result.scalars().all()
|
||||
|
||||
if not difficulties_db:
|
||||
return None
|
||||
return self.mapper.story_episode_difficulties_to_dto(difficulties_db)
|
||||
|
||||
async def put_story(self, aime_id: int, story_data: StoryDto) -> Optional[int]:
|
||||
story_data_db = story_data.model_dump_db()
|
||||
story_data_db["user"] = aime_id
|
||||
|
||||
sql = insert(StoryDb).values(story_data_db)
|
||||
conflict = sql.on_duplicate_key_update(story_data_db)
|
||||
|
||||
result = await self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warning(f"put_story: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_story_episode_play_status(
|
||||
self, aime_id: int, chapter_id: int, play_status: int = 1
|
||||
) -> Optional[int]:
|
||||
sql = (
|
||||
update(StoryEpisodeDb)
|
||||
.where(
|
||||
and_(
|
||||
StoryEpisodeDb.user == aime_id, StoryEpisodeDb.chapter == chapter_id
|
||||
)
|
||||
)
|
||||
.values(play_status=play_status)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_story_episode_play_status: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_story_episode(
|
||||
self, aime_id: int, chapter_id: int, episode_data: StoryEpisodeDto
|
||||
) -> Optional[int]:
|
||||
episode_data_db = episode_data.model_dump_db()
|
||||
episode_data_db["user"] = aime_id
|
||||
episode_data_db["chapter"] = chapter_id
|
||||
|
||||
sql = insert(StoryEpisodeDb).values(episode_data_db)
|
||||
conflict = sql.on_duplicate_key_update(episode_data_db)
|
||||
|
||||
result = await self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_story_episode: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
async def put_story_episode_difficulty(
|
||||
self, aime_id: int, episode_id: int, difficulty_data: StoryEpisodeDifficultyDto
|
||||
) -> Optional[int]:
|
||||
difficulty_data_db = difficulty_data.model_dump_db()
|
||||
difficulty_data_db["user"] = aime_id
|
||||
difficulty_data_db["episode"] = episode_id
|
||||
|
||||
sql = insert(StoryEpisodeDifficultyDb).values(difficulty_data_db)
|
||||
conflict = sql.on_duplicate_key_update(difficulty_data_db)
|
||||
|
||||
result = await self.execute(conflict)
|
||||
if result is None:
|
||||
self.logger.warning(
|
||||
f"put_story_episode_difficulty: Failed to update! aime_id: {aime_id}"
|
||||
)
|
||||
return None
|
||||
return result.lastrowid
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user