diff --git a/changelog.md b/changelog.md index 40d2e48..75ca217 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,13 @@ # Changelog Documenting updates to ARTEMiS, to be updated every time the master branch is pushed to. +## 20231015 +### maimai DX ++ Added support for FESTiVAL PLUS + +### Card Maker ++ Added support for maimai DX FESTiVAL PLUS + ## 20230716 ### General + Docker files added (#19) diff --git a/core/data/schema/versions/SDEZ_7_rollback.sql b/core/data/schema/versions/SDEZ_7_rollback.sql new file mode 100644 index 0000000..6bb9270 --- /dev/null +++ b/core/data/schema/versions/SDEZ_7_rollback.sql @@ -0,0 +1,10 @@ +ALTER TABLE mai2_profile_detail + DROP COLUMN mapStock; + +ALTER TABLE mai2_profile_extend + DROP COLUMN selectResultScoreViewType; + +ALTER TABLE mai2_profile_option + DROP COLUMN outFrameType, + DROP COLUMN touchVolume, + DROP COLUMN breakSlideVolume; diff --git a/core/data/schema/versions/SDEZ_8_upgrade.sql b/core/data/schema/versions/SDEZ_8_upgrade.sql new file mode 100644 index 0000000..c782d81 --- /dev/null +++ b/core/data/schema/versions/SDEZ_8_upgrade.sql @@ -0,0 +1,10 @@ +ALTER TABLE mai2_profile_detail + ADD mapStock INT NULL AFTER playCount; + +ALTER TABLE mai2_profile_extend + ADD selectResultScoreViewType INT NULL AFTER selectResultDetails; + +ALTER TABLE mai2_profile_option + ADD outFrameType INT NULL AFTER dispCenter, + ADD touchVolume INT NULL AFTER slideVolume, + ADD breakSlideVolume INT NULL AFTER slideVolume; diff --git a/docs/game_specific_info.md b/docs/game_specific_info.md index ac27f55..9f39a7e 100644 --- a/docs/game_specific_info.md +++ b/docs/game_specific_info.md @@ -194,18 +194,19 @@ For versions pre-dx | SDBM | 5 | maimai ORANGE PLUS | | SDCQ | 6 | maimai PiNK | | SDCQ | 7 | maimai PiNK PLUS | -| SDDK | 8 | maimai MURASAKI | -| SDDK | 9 | maimai MURASAKI PLUS | -| SDDZ | 10 | maimai MILK | -| SDDZ | 11 | maimai MILK PLUS | +| SDDK | 8 | maimai MURASAKi | +| SDDK | 9 | maimai MURASAKi PLUS | +| SDDZ | 10 | maimai MiLK | +| SDDZ | 11 | maimai MiLK PLUS | | SDEY | 12 | maimai FiNALE | | SDEZ | 13 | maimai DX | | SDEZ | 14 | maimai DX PLUS | | SDEZ | 15 | maimai DX Splash | | SDEZ | 16 | maimai DX Splash PLUS | -| SDEZ | 17 | maimai DX Universe | -| SDEZ | 18 | maimai DX Universe PLUS | -| SDEZ | 19 | maimai DX Festival | +| SDEZ | 17 | maimai DX UNiVERSE | +| SDEZ | 18 | maimai DX UNiVERSE PLUS | +| SDEZ | 19 | maimai DX FESTiVAL | +| SDEZ | 20 | maimai DX FESTiVAL PLUS | ### Importer @@ -347,15 +348,21 @@ python dbutils.py --game SDDT upgrade ### Support status -* Card Maker 1.30: - * CHUNITHM NEW!!: Yes - * maimai DX UNiVERSE: Yes - * O.N.G.E.K.I. bright: Yes +#### Card Maker 1.30: +* CHUNITHM NEW!!: Yes +* maimai DX UNiVERSE: Yes +* O.N.G.E.K.I. bright: Yes -* Card Maker 1.35: - * CHUNITHM SUN: Yes (NEW PLUS!! up to A032) - * maimai DX FESTiVAL: Yes (up to A035) (UNiVERSE PLUS up to A031) - * O.N.G.E.K.I. bright MEMORY: Yes +#### Card Maker 1.35: +* CHUNITHM: + * NEW!!: Yes + * NEW PLUS!!: Yes (added in A028) + * SUN: Yes (added in A032) +* maimai DX: + * UNiVERSE PLUS: Yes + * FESTiVAL: Yes (added in A031) + * FESTiVAL PLUS: Yes (added in A035) +* O.N.G.E.K.I. bright MEMORY: Yes ### Importer diff --git a/readme.md b/readme.md index 2c49faa..2997338 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ Games listed below have been tested and confirmed working. Only game versions ol + All versions + omnimix + maimai DX - + All versions up to FESTiVAL + + All versions up to FESTiVAL PLUS + Hatsune Miku: Project DIVA Arcade + All versions diff --git a/titles/cm/read.py b/titles/cm/read.py index 059010e..376789d 100644 --- a/titles/cm/read.py +++ b/titles/cm/read.py @@ -205,6 +205,7 @@ class CardMakerReader(BaseReader): "1.20": Mai2Constants.VER_MAIMAI_DX_UNIVERSE, "1.25": Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS, "1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL, + "1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS, } for root, dirs, files in os.walk(base_dir): diff --git a/titles/mai2/__init__.py b/titles/mai2/__init__.py index 2fa3874..4857644 100644 --- a/titles/mai2/__init__.py +++ b/titles/mai2/__init__.py @@ -16,4 +16,4 @@ game_codes = [ Mai2Constants.GAME_CODE_GREEN, Mai2Constants.GAME_CODE, ] -current_schema_version = 7 +current_schema_version = 8 diff --git a/titles/mai2/const.py b/titles/mai2/const.py index 6e9a070..a4c29db 100644 --- a/titles/mai2/const.py +++ b/titles/mai2/const.py @@ -20,13 +20,13 @@ class Mai2Constants: DATE_TIME_FORMAT = "%Y-%m-%d %H:%M:%S" - GAME_CODE = "SBXL" - GAME_CODE_GREEN = "SBZF" - GAME_CODE_ORANGE = "SDBM" - GAME_CODE_PINK = "SDCQ" - GAME_CODE_MURASAKI = "SDDK" - GAME_CODE_MILK = "SDDZ" - GAME_CODE_FINALE = "SDEY" + GAME_CODE = "SBXL" + GAME_CODE_GREEN = "SBZF" + GAME_CODE_ORANGE = "SDBM" + GAME_CODE_PINK = "SDCQ" + GAME_CODE_MURASAKI = "SDDK" + GAME_CODE_MILK = "SDDZ" + GAME_CODE_FINALE = "SDEY" GAME_CODE_DX = "SDEZ" CONFIG_NAME = "mai2.yaml" @@ -52,6 +52,7 @@ class Mai2Constants: VER_MAIMAI_DX_UNIVERSE = 17 VER_MAIMAI_DX_UNIVERSE_PLUS = 18 VER_MAIMAI_DX_FESTIVAL = 19 + VER_MAIMAI_DX_FESTIVAL_PLUS = 20 VERSION_STRING = ( "maimai", @@ -66,7 +67,7 @@ class Mai2Constants: "maimai MURASAKi PLUS", "maimai MiLK", "maimai MiLK PLUS", - "maimai FiNALE", + "maimai FiNALE", "maimai DX", "maimai DX PLUS", "maimai DX Splash", @@ -74,6 +75,7 @@ class Mai2Constants: "maimai DX UNiVERSE", "maimai DX UNiVERSE PLUS", "maimai DX FESTiVAL", + "maimai DX FESTiVAL PLUS", ) @classmethod diff --git a/titles/mai2/dx.py b/titles/mai2/dx.py index e373649..3feccc5 100644 --- a/titles/mai2/dx.py +++ b/titles/mai2/dx.py @@ -542,8 +542,43 @@ class Mai2DX(Mai2Base): } def handle_get_user_region_api_request(self, data: Dict) -> Dict: + """ + class UserRegionList: + regionId: int + playCount: int + created: str + """ return {"userId": data["userId"], "length": 0, "userRegionList": []} + def handle_get_user_rival_data_api_request(self, data: Dict) -> Dict: + user_id = data["userId"] + rival_id = data["rivalId"] + + """ + class UserRivalData: + rivalId: int + rivalName: str + """ + return {"userId": user_id, "userRivalData": {}} + + def handle_get_user_rival_music_api_request(self, data: Dict) -> Dict: + user_id = data["userId"] + rival_id = data["rivalId"] + next_idx = data["nextIndex"] + rival_music_levels = data["userRivalMusicLevelList"] + + """ + class UserRivalMusicList: + class UserRivalMusicDetailList: + level: int + achievement: int + deluxscoreMax: int + + musicId: int + userRivalMusicDetailList: list[UserRivalMusicDetailList] + """ + return {"userId": user_id, "nextIndex": 0, "userRivalMusicList": []} + def handle_get_user_music_api_request(self, data: Dict) -> Dict: user_id = data.get("userId", 0) next_index = data.get("nextIndex", 0) diff --git a/titles/mai2/festival.py b/titles/mai2/festival.py index fcdd4ed..145fa71 100644 --- a/titles/mai2/festival.py +++ b/titles/mai2/festival.py @@ -14,7 +14,7 @@ class Mai2Festival(Mai2UniversePlus): def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: user_data = super().handle_cm_get_user_preview_api_request(data) - # hardcode lastDataVersion for CardMaker 1.35 + # hardcode lastDataVersion for CardMaker user_data["lastDataVersion"] = "1.30.00" return user_data @@ -25,7 +25,13 @@ class Mai2Festival(Mai2UniversePlus): return user_login def handle_get_user_recommend_rate_music_api_request(self, data: Dict) -> Dict: + """ + userRecommendRateMusicIdList: list[int] + """ return {"userId": data["userId"], "userRecommendRateMusicIdList": []} def handle_get_user_recommend_select_music_api_request(self, data: Dict) -> Dict: + """ + userRecommendSelectionMusicIdList: list[int] + """ return {"userId": data["userId"], "userRecommendSelectionMusicIdList": []} diff --git a/titles/mai2/festivalplus.py b/titles/mai2/festivalplus.py new file mode 100644 index 0000000..7deeb98 --- /dev/null +++ b/titles/mai2/festivalplus.py @@ -0,0 +1,38 @@ +from typing import Dict + +from core.config import CoreConfig +from titles.mai2.festival import Mai2Festival +from titles.mai2.const import Mai2Constants +from titles.mai2.config import Mai2Config + + +class Mai2FestivalPlus(Mai2Festival): + def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None: + super().__init__(cfg, game_cfg) + self.version = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS + + def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + user_data = super().handle_cm_get_user_preview_api_request(data) + + # hardcode lastDataVersion for CardMaker + user_data["lastDataVersion"] = "1.35.00" + return user_data + + def handle_get_user_favorite_item_api_request(self, data: Dict) -> Dict: + user_id = data.get("userId", 0) + kind = data.get("kind", 2) + next_index = data.get("nextIndex", 0) + max_ct = data.get("maxCount", 100) + is_all = data.get("isAllFavoriteItem", False) + + """ + class userFavoriteItemList: + orderId: int + id: int + """ + return { + "userId": user_id, + "kind": kind, + "nextIndex": 0, + "userFavoriteItemList": [], + } diff --git a/titles/mai2/index.py b/titles/mai2/index.py index 329c1f6..fdea8db 100644 --- a/titles/mai2/index.py +++ b/titles/mai2/index.py @@ -23,6 +23,7 @@ from titles.mai2.splashplus import Mai2SplashPlus from titles.mai2.universe import Mai2Universe from titles.mai2.universeplus import Mai2UniversePlus from titles.mai2.festival import Mai2Festival +from titles.mai2.festivalplus import Mai2FestivalPlus class Mai2Servlet: @@ -47,7 +48,7 @@ class Mai2Servlet: None, None, None, - Mai2Finale, + Mai2Finale, Mai2DX, Mai2DXPlus, Mai2Splash, @@ -55,6 +56,7 @@ class Mai2Servlet: Mai2Universe, Mai2UniversePlus, Mai2Festival, + Mai2FestivalPlus, ] self.logger = logging.getLogger("mai2") @@ -110,22 +112,34 @@ class Mai2Servlet: ) def setup(self): - if self.game_cfg.uploads.photos and self.game_cfg.uploads.photos_dir and not path.exists(self.game_cfg.uploads.photos_dir): + if ( + self.game_cfg.uploads.photos + and self.game_cfg.uploads.photos_dir + and not path.exists(self.game_cfg.uploads.photos_dir) + ): try: mkdir(self.game_cfg.uploads.photos_dir) except Exception: - self.logger.error(f"Failed to make photo upload directory at {self.game_cfg.uploads.photos_dir}") + self.logger.error( + f"Failed to make photo upload directory at {self.game_cfg.uploads.photos_dir}" + ) - if self.game_cfg.uploads.movies and self.game_cfg.uploads.movies_dir and not path.exists(self.game_cfg.uploads.movies_dir): + if ( + self.game_cfg.uploads.movies + and self.game_cfg.uploads.movies_dir + and not path.exists(self.game_cfg.uploads.movies_dir) + ): try: mkdir(self.game_cfg.uploads.movies_dir) except Exception: - self.logger.error(f"Failed to make movie upload directory at {self.game_cfg.uploads.movies_dir}") + self.logger.error( + f"Failed to make movie upload directory at {self.game_cfg.uploads.movies_dir}" + ) def render_POST(self, request: Request, version: int, url_path: str) -> bytes: if url_path.lower() == "ping": return zlib.compress(b'{"returnCode": "1"}') - + elif url_path.startswith("api/movie/"): self.logger.info(f"Movie data: {url_path} - {request.content.getvalue()}") return b"" @@ -140,18 +154,20 @@ class Mai2Servlet: if request.uri.startswith(b"/SDEZ"): if version < 105: # 1.0 internal_ver = Mai2Constants.VER_MAIMAI_DX - elif version >= 105 and version < 110: # Plus + elif version >= 105 and version < 110: # PLUS internal_ver = Mai2Constants.VER_MAIMAI_DX_PLUS elif version >= 110 and version < 115: # Splash internal_ver = Mai2Constants.VER_MAIMAI_DX_SPLASH - elif version >= 115 and version < 120: # Splash Plus + elif version >= 115 and version < 120: # Splash PLUS internal_ver = Mai2Constants.VER_MAIMAI_DX_SPLASH_PLUS - elif version >= 120 and version < 125: # Universe + elif version >= 120 and version < 125: # UNiVERSE internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE - elif version >= 125 and version < 130: # Universe Plus + elif version >= 125 and version < 130: # UNiVERSE PLUS internal_ver = Mai2Constants.VER_MAIMAI_DX_UNIVERSE_PLUS - elif version >= 130: # Festival + elif version >= 130 and version < 135: # FESTiVAL internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL + elif version >= 135: # FESTiVAL PLUS + internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS else: if version < 110: # 1.0 @@ -180,15 +196,20 @@ class Mai2Servlet: internal_ver = Mai2Constants.VER_MAIMAI_MILK_PLUS elif version >= 197: # Finale internal_ver = Mai2Constants.VER_MAIMAI_FINALE - - if request.getHeader('Mai-Encoding') is not None or request.getHeader('X-Mai-Encoding') is not None: + + if ( + request.getHeader("Mai-Encoding") is not None + or request.getHeader("X-Mai-Encoding") is not None + ): # The has is some flavor of MD5 of the endpoint with a constant bolted onto the end of it. # See cake.dll's Obfuscator function for details. Hopefully most DLL edits will remove # these two(?) headers to not cause issues, but given the general quality of SEGA data... - enc_ver = request.getHeader('Mai-Encoding') + enc_ver = request.getHeader("Mai-Encoding") if enc_ver is None: - enc_ver = request.getHeader('X-Mai-Encoding') - self.logger.debug(f"Encryption v{enc_ver} - User-Agent: {request.getHeader('User-Agent')}") + enc_ver = request.getHeader("X-Mai-Encoding") + self.logger.debug( + f"Encryption v{enc_ver} - User-Agent: {request.getHeader('User-Agent')}" + ) try: unzip = zlib.decompress(req_raw) @@ -231,10 +252,12 @@ class Mai2Servlet: self.logger.debug(f"v{version} GET {url_path}") url_split = url_path.split("/") - if (url_split[0] == "api" and url_split[1] == "movie") or url_split[0] == "movie": + if (url_split[0] == "api" and url_split[1] == "movie") or url_split[ + 0 + ] == "movie": if url_split[2] == "moviestart": - return json.dumps({"moviestart":{"status":"OK"}}).encode() - + return json.dumps({"moviestart": {"status": "OK"}}).encode() + else: request.setResponseCode(404) return b"" @@ -251,20 +274,24 @@ class Mai2Servlet: elif url_split[1].startswith("friend"): self.logger.info(f"v{version} old server friend inquire") return zlib.compress(b"{}") - + else: request.setResponseCode(404) return b"" - + elif url_split[0] == "usbdl": if url_split[1] == "CONNECTIONTEST": self.logger.info(f"v{version} usbdl server test") return b"" - - elif self.game_cfg.deliver.udbdl_enable and path.exists(f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}"): - with open(f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}", 'rb') as f: + + elif self.game_cfg.deliver.udbdl_enable and path.exists( + f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}" + ): + with open( + f"{self.game_cfg.deliver.content_folder}/usb/{url_split[-1]}", "rb" + ) as f: return f.read() - + else: request.setResponseCode(404) return b"" @@ -272,12 +299,18 @@ class Mai2Servlet: elif url_split[0] == "deliver": file = url_split[len(url_split) - 1] self.logger.info(f"v{version} {file} deliver inquire") - self.logger.debug(f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}") - - if self.game_cfg.deliver.enable and path.exists(f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}"): - with open(f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}", 'rb') as f: + self.logger.debug( + f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}" + ) + + if self.game_cfg.deliver.enable and path.exists( + f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}" + ): + with open( + f"{self.game_cfg.deliver.content_folder}/net_deliver/{file}", "rb" + ) as f: return f.read() - + else: request.setResponseCode(404) return b"" diff --git a/titles/mai2/schema/profile.py b/titles/mai2/schema/profile.py index 2b3f38b..211440d 100644 --- a/titles/mai2/schema/profile.py +++ b/titles/mai2/schema/profile.py @@ -40,6 +40,7 @@ detail = Table( Column("charaLockSlot", JSON), Column("contentBit", BigInteger), Column("playCount", Integer), + Column("mapStock", Integer), # new with fes+ Column("eventWatchedDate", String(25)), Column("lastGameId", String(25)), Column("lastRomVersion", String(25)), @@ -100,7 +101,7 @@ detail = Table( ) detail_old = Table( - "maimai_profile_detail", + "maimai_profile_detail", metadata, Column("id", Integer, primary_key=True, nullable=False), Column( @@ -216,6 +217,7 @@ extend = Table( Column("isPhotoAgree", Boolean), Column("isGotoCodeRead", Boolean), Column("selectResultDetails", Boolean), + Column("selectResultScoreViewType", Integer), # new with fes+ Column("sortCategorySetting", Integer), Column("sortMusicSetting", Integer), Column("selectedCardList", JSON), @@ -251,6 +253,7 @@ option = Table( Column("touchSize", Integer), Column("starRotate", Integer), Column("dispCenter", Integer), + Column("outFrameType", Integer), # new with fes+ Column("dispChain", Integer), Column("dispRate", Integer), Column("dispBar", Integer), @@ -276,6 +279,8 @@ option = Table( Column("exVolume", Integer), Column("slideSe", Integer), Column("slideVolume", Integer), + Column("breakSlideVolume", Integer), # new with fes+ + Column("touchVolume", Integer), # new with fes+ Column("touchHoldVolume", Integer), Column("damageSeVolume", Integer), Column("headPhoneVolume", Integer), @@ -438,7 +443,11 @@ boss = Table( "maimai_profile_boss", metadata, Column("id", Integer, primary_key=True, nullable=False), - Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column( + "user", + ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), + nullable=False, + ), Column("pandoraFlagList0", Integer), Column("pandoraFlagList1", Integer), Column("pandoraFlagList2", Integer), @@ -455,23 +464,32 @@ recent_rating = Table( "maimai_profile_recent_rating", metadata, Column("id", Integer, primary_key=True, nullable=False), - Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column( + "user", + ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), + nullable=False, + ), Column("userRecentRatingList", JSON), UniqueConstraint("user", name="mai2_profile_recent_rating_uk"), mysql_charset="utf8mb4", ) consec_logins = Table( - "mai2_profile_consec_logins", + "mai2_profile_consec_logins", metadata, Column("id", Integer, primary_key=True, nullable=False), - Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False), + Column( + "user", + ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), + nullable=False, + ), Column("version", Integer, nullable=False), Column("logins", Integer), UniqueConstraint("user", "version", name="mai2_profile_consec_logins_uk"), mysql_charset="utf8mb4", ) + class Mai2ProfileData(BaseData): def put_profile_detail( self, user_id: int, version: int, detail_data: Dict, is_dx: bool = True @@ -494,18 +512,22 @@ class Mai2ProfileData(BaseData): return None return result.lastrowid - def get_profile_detail(self, user_id: int, version: int, is_dx: bool = True) -> Optional[Row]: + def get_profile_detail( + self, user_id: int, version: int, is_dx: bool = True + ) -> Optional[Row]: if is_dx: sql = ( select(detail) .where(and_(detail.c.user == user_id, detail.c.version <= version)) .order_by(detail.c.version.desc()) ) - + else: sql = ( select(detail_old) - .where(and_(detail_old.c.user == user_id, detail_old.c.version <= version)) + .where( + and_(detail_old.c.user == user_id, detail_old.c.version <= version) + ) .order_by(detail_old.c.version.desc()) ) @@ -582,11 +604,15 @@ class Mai2ProfileData(BaseData): result = self.execute(conflict) if result is None: - self.logger.warning(f"put_profile_option: failed to update! {user_id} is_dx {is_dx}") + self.logger.warning( + f"put_profile_option: failed to update! {user_id} is_dx {is_dx}" + ) return None return result.lastrowid - def get_profile_option(self, user_id: int, version: int, is_dx: bool = True) -> Optional[Row]: + def get_profile_option( + self, user_id: int, version: int, is_dx: bool = True + ) -> Optional[Row]: if is_dx: sql = ( select(option) @@ -596,7 +622,9 @@ class Mai2ProfileData(BaseData): else: sql = ( select(option_old) - .where(and_(option_old.c.user == user_id, option_old.c.version <= version)) + .where( + and_(option_old.c.user == user_id, option_old.c.version <= version) + ) .order_by(option_old.c.version.desc()) ) @@ -689,7 +717,9 @@ class Mai2ProfileData(BaseData): return None return result.fetchall() - def put_web_option(self, user_id: int, version: int, web_opts: Dict) -> Optional[int]: + def put_web_option( + self, user_id: int, version: int, web_opts: Dict + ) -> Optional[int]: web_opts["user"] = user_id web_opts["version"] = version sql = insert(web_opt).values(**web_opts) @@ -698,14 +728,14 @@ class Mai2ProfileData(BaseData): result = self.execute(conflict) if result is None: - self.logger.warning( - f"put_web_option: failed to update! user_id: {user_id}" - ) + self.logger.warning(f"put_web_option: failed to update! user_id: {user_id}") return None return result.lastrowid - + def get_web_option(self, user_id: int, version: int) -> Optional[Row]: - sql = web_opt.select(and_(web_opt.c.user == user_id, web_opt.c.version == version)) + sql = web_opt.select( + and_(web_opt.c.user == user_id, web_opt.c.version == version) + ) result = self.execute(sql) if result is None: @@ -725,7 +755,7 @@ class Mai2ProfileData(BaseData): ) return None return result.lastrowid - + def get_grade_status(self, user_id: int) -> Optional[Row]: sql = grade_status.select(grade_status.c.user == user_id) @@ -742,12 +772,10 @@ class Mai2ProfileData(BaseData): result = self.execute(conflict) if result is None: - self.logger.warning( - f"put_boss_list: failed to update! user_id: {user_id}" - ) + self.logger.warning(f"put_boss_list: failed to update! user_id: {user_id}") return None return result.lastrowid - + def get_boss_list(self, user_id: int) -> Optional[Row]: sql = boss.select(boss.c.user == user_id) @@ -759,7 +787,7 @@ class Mai2ProfileData(BaseData): def put_recent_rating(self, user_id: int, rr: Dict) -> Optional[int]: sql = insert(recent_rating).values(user=user_id, userRecentRatingList=rr) - conflict = sql.on_duplicate_key_update({'userRecentRatingList': rr}) + conflict = sql.on_duplicate_key_update({"userRecentRatingList": rr}) result = self.execute(conflict) if result is None: @@ -768,7 +796,7 @@ class Mai2ProfileData(BaseData): ) return None return result.lastrowid - + def get_recent_rating(self, user_id: int) -> Optional[Row]: sql = recent_rating.select(recent_rating.c.user == user_id) @@ -776,27 +804,25 @@ class Mai2ProfileData(BaseData): if result is None: return None return result.fetchone() - - def add_consec_login(self, user_id: int, version: int) -> None: - sql = insert(consec_logins).values( - user=user_id, - version=version, - logins=1 - ) - conflict = sql.on_duplicate_key_update( - logins=consec_logins.c.logins + 1 - ) + def add_consec_login(self, user_id: int, version: int) -> None: + sql = insert(consec_logins).values(user=user_id, version=version, logins=1) + + conflict = sql.on_duplicate_key_update(logins=consec_logins.c.logins + 1) result = self.execute(conflict) if result is None: - self.logger.error(f"Failed to update consecutive login count for user {user_id} version {version}") + self.logger.error( + f"Failed to update consecutive login count for user {user_id} version {version}" + ) def get_consec_login(self, user_id: int, version: int) -> Optional[Row]: - sql = select(consec_logins).where(and_( - consec_logins.c.user==user_id, - consec_logins.c.version==version, - )) + sql = select(consec_logins).where( + and_( + consec_logins.c.user == user_id, + consec_logins.c.version == version, + ) + ) result = self.execute(sql) if result is None: @@ -804,12 +830,12 @@ class Mai2ProfileData(BaseData): return result.fetchone() def reset_consec_login(self, user_id: int, version: int) -> Optional[Row]: - sql = consec_logins.update(and_( - consec_logins.c.user==user_id, - consec_logins.c.version==version, - )).values( - logins=1 - ) + sql = consec_logins.update( + and_( + consec_logins.c.user == user_id, + consec_logins.c.version == version, + ) + ).values(logins=1) result = self.execute(sql) if result is None: diff --git a/titles/mai2/universe.py b/titles/mai2/universe.py index c17f899..d25a295 100644 --- a/titles/mai2/universe.py +++ b/titles/mai2/universe.py @@ -23,10 +23,11 @@ class Mai2Universe(Mai2SplashPlus): return { "userName": p["userName"], "rating": p["playerRating"], - # hardcode lastDataVersion for CardMaker 1.34 + # hardcode lastDataVersion for CardMaker "lastDataVersion": "1.20.00", + # checks if the user is still logged in "isLogin": False, - "isExistSellingCard": False, + "isExistSellingCard": True, } def handle_cm_get_user_data_api_request(self, data: Dict) -> Dict: @@ -70,8 +71,12 @@ class Mai2Universe(Mai2SplashPlus): tmp.pop("cardName") tmp.pop("enabled") - tmp["startDate"] = datetime.strftime(tmp["startDate"], Mai2Constants.DATE_TIME_FORMAT) - tmp["endDate"] = datetime.strftime(tmp["endDate"], Mai2Constants.DATE_TIME_FORMAT) + tmp["startDate"] = datetime.strftime( + tmp["startDate"], Mai2Constants.DATE_TIME_FORMAT + ) + tmp["endDate"] = datetime.strftime( + tmp["endDate"], Mai2Constants.DATE_TIME_FORMAT + ) tmp["noticeStartDate"] = datetime.strftime( tmp["noticeStartDate"], Mai2Constants.DATE_TIME_FORMAT )