From ee4eddd63962993e17d4efe89cc7bf038478a5b3 Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 15 Sep 2024 19:22:39 +0000 Subject: [PATCH 01/24] add buddies plus support --- titles/mai2/buddiesplus.py | 36 ++++++++++++++++++++++++++++++++++++ titles/mai2/const.py | 4 +++- titles/mai2/index.py | 8 ++++++-- 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 titles/mai2/buddiesplus.py diff --git a/titles/mai2/buddiesplus.py b/titles/mai2/buddiesplus.py new file mode 100644 index 0000000..0467b03 --- /dev/null +++ b/titles/mai2/buddiesplus.py @@ -0,0 +1,36 @@ +from typing import Dict + +from core.config import CoreConfig +from titles.mai2.buddies import Mai2Buddies +from titles.mai2.const import Mai2Constants +from titles.mai2.config import Mai2Config + + +class Mai2BuddiesPlus(Mai2Buddies): + def __init__(self, cfg: CoreConfig, game_cfg: Mai2Config) -> None: + super().__init__(cfg, game_cfg) + self.version = Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS + + async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: + user_data = await super().handle_cm_get_user_preview_api_request(data) + + # hardcode lastDataVersion for CardMaker + user_data["lastDataVersion"] = "1.45.00" + return user_data + + async def handle_get_game_weekly_data_api_request(self, data: Dict) -> Dict: + return { + "gameWeeklyData": { + "missionCategory": 0, + "updateDate": "2024-03-21 09:00:00.0", + "beforeDate": "2099-12-31 00:00:00.0" + } + } + + async def handle_create_token_api_request(self, data: Dict) -> Dict: + return { + "Bearer": "ARTEMiSTOKEN" # duplicate of handle_user_login_api_request from Mai2Festival + } + + async def handle_remove_token_api_request(self, data: Dict) -> Dict: + return {} diff --git a/titles/mai2/const.py b/titles/mai2/const.py index 4dc10ce..0d13a0d 100644 --- a/titles/mai2/const.py +++ b/titles/mai2/const.py @@ -55,6 +55,7 @@ class Mai2Constants: VER_MAIMAI_DX_FESTIVAL = 19 VER_MAIMAI_DX_FESTIVAL_PLUS = 20 VER_MAIMAI_DX_BUDDIES = 21 + VER_MAIMAI_DX_BUDDIES_PLUS = 22 VERSION_STRING = ( "maimai", @@ -78,7 +79,8 @@ class Mai2Constants: "maimai DX UNiVERSE PLUS", "maimai DX FESTiVAL", "maimai DX FESTiVAL PLUS", - "maimai DX BUDDiES" + "maimai DX BUDDiES", + "maimai DX BUDDiES PLUS" ) @classmethod diff --git a/titles/mai2/index.py b/titles/mai2/index.py index ad02648..e8b88ec 100644 --- a/titles/mai2/index.py +++ b/titles/mai2/index.py @@ -30,6 +30,7 @@ from .universeplus import Mai2UniversePlus from .festival import Mai2Festival from .festivalplus import Mai2FestivalPlus from .buddies import Mai2Buddies +from .buddiesplus import Mai2BuddiesPlus class Mai2Servlet(BaseServlet): @@ -64,7 +65,8 @@ class Mai2Servlet(BaseServlet): Mai2UniversePlus, Mai2Festival, Mai2FestivalPlus, - Mai2Buddies + Mai2Buddies, + Mai2BuddiesPlus ] self.logger = logging.getLogger("mai2") @@ -302,8 +304,10 @@ class Mai2Servlet(BaseServlet): internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL elif version >= 135 and version < 140: # FESTiVAL PLUS internal_ver = Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS - elif version >= 140: # BUDDiES + elif version >= 140 and version < 145: # BUDDiES internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES + elif version >= 145: # BUDDiES PLUS + internal_ver = Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS elif game_code == "SDGA": # Int if version < 105: # 1.0 internal_ver = Mai2Constants.VER_MAIMAI_DX From cc302b6e56e5eb43b761ccc6a5a6a7f5b7de7178 Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 15 Sep 2024 19:22:47 +0000 Subject: [PATCH 02/24] update cm reader --- titles/cm/read.py | 1 + 1 file changed, 1 insertion(+) diff --git a/titles/cm/read.py b/titles/cm/read.py index 2b5ec8a..b4b3b5e 100644 --- a/titles/cm/read.py +++ b/titles/cm/read.py @@ -207,6 +207,7 @@ class CardMakerReader(BaseReader): "1.30": Mai2Constants.VER_MAIMAI_DX_FESTIVAL, "1.35": Mai2Constants.VER_MAIMAI_DX_FESTIVAL_PLUS, "1.40": Mai2Constants.VER_MAIMAI_DX_BUDDIES, + "1.45": Mai2Constants.VER_MAIMAI_DX_BUDDIES_PLUS } for root, dirs, files in os.walk(base_dir): From 01dad267b9962ee87edabdd2bb91b5a518e46445 Mon Sep 17 00:00:00 2001 From: ppc Date: Sun, 15 Sep 2024 20:48:24 +0000 Subject: [PATCH 03/24] add mai2 database upgrade --- .../28443e2da5b8_mai2_buddies_plus.py | 28 +++++++++++++++++++ titles/mai2/schema/profile.py | 5 +++- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 core/data/alembic/versions/28443e2da5b8_mai2_buddies_plus.py diff --git a/core/data/alembic/versions/28443e2da5b8_mai2_buddies_plus.py b/core/data/alembic/versions/28443e2da5b8_mai2_buddies_plus.py new file mode 100644 index 0000000..42fcdde --- /dev/null +++ b/core/data/alembic/versions/28443e2da5b8_mai2_buddies_plus.py @@ -0,0 +1,28 @@ +"""mai2_buddies_plus + +Revision ID: 28443e2da5b8 +Revises: 5ea73f89d982 +Create Date: 2024-09-15 20:44:02.351819 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '28443e2da5b8' +down_revision = '5ea73f89d982' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column('mai2_profile_detail', sa.Column('point', sa.Integer())) + op.add_column('mai2_profile_detail', sa.Column('totalPoint', sa.Integer())) + op.add_column('mai2_profile_detail', sa.Column('friendRegistSkip', sa.SmallInteger())) + + +def downgrade(): + op.drop_column('mai2_profile_detail', 'point') + op.drop_column('mai2_profile_detail', 'totalPoint') + op.drop_column('mai2_profile_detail', 'friendRegistSkip') diff --git a/titles/mai2/schema/profile.py b/titles/mai2/schema/profile.py index c191a1a..988dcb0 100644 --- a/titles/mai2/schema/profile.py +++ b/titles/mai2/schema/profile.py @@ -3,7 +3,7 @@ from titles.mai2.const import Mai2Constants from typing import Optional, Dict, List from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_ -from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger +from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger, SmallInteger from sqlalchemy.schema import ForeignKey from sqlalchemy.sql import func, select from sqlalchemy.engine import Row @@ -43,6 +43,8 @@ detail = Table( Column("currentPlayCount", Integer), # new with buddies Column("renameCredit", Integer), # new with buddies Column("mapStock", Integer), # new with fes+ + Column("point", Integer), # new with buddies+ + Column("totalPoint", Integer), # new with buddies+ Column("eventWatchedDate", String(25)), Column("lastGameId", String(25)), Column("lastRomVersion", String(25)), @@ -97,6 +99,7 @@ detail = Table( Column("playerOldRating", BigInteger), Column("playerNewRating", BigInteger), Column("dateTime", BigInteger), + Column("friendRegistSkip", SmallInteger), # new with buddies+ Column("banState", Integer), # new with uni+ UniqueConstraint("user", "version", name="mai2_profile_detail_uk"), mysql_charset="utf8mb4", From b01ac24799cf98cd0cebd2184f8cf1b3329d598b Mon Sep 17 00:00:00 2001 From: ppc Date: Mon, 16 Sep 2024 09:54:39 +0000 Subject: [PATCH 04/24] add shop stock/friend bonus handlers --- titles/mai2/buddiesplus.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/titles/mai2/buddiesplus.py b/titles/mai2/buddiesplus.py index 0467b03..a3784d5 100644 --- a/titles/mai2/buddiesplus.py +++ b/titles/mai2/buddiesplus.py @@ -22,15 +22,28 @@ class Mai2BuddiesPlus(Mai2Buddies): return { "gameWeeklyData": { "missionCategory": 0, - "updateDate": "2024-03-21 09:00:00.0", - "beforeDate": "2099-12-31 00:00:00.0" + "updateDate": "2024-03-21 09:00:00", + "beforeDate": "2099-12-31 00:00:00" } } - + async def handle_create_token_api_request(self, data: Dict) -> Dict: return { "Bearer": "ARTEMiSTOKEN" # duplicate of handle_user_login_api_request from Mai2Festival } - + async def handle_remove_token_api_request(self, data: Dict) -> Dict: return {} + + async def handle_get_user_friend_bonus_api_request(self, data: Dict) -> Dict: + return { + "userId": data["userId"], + "returnCode": 1, + "getMiles": 0 + } + + async def handle_get_user_shop_stock_api_request(self, data: Dict) -> Dict: + return { + "userId": data["userId"], + "userShopStockList": [] + } From e128631e8fe83f0d0a14568d3ad57db27ec8b9e9 Mon Sep 17 00:00:00 2001 From: ppc Date: Mon, 16 Sep 2024 12:39:15 +0100 Subject: [PATCH 05/24] fix upsert failures --- titles/mai2/dx.py | 3 ++- titles/mai2/schema/item.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/titles/mai2/dx.py b/titles/mai2/dx.py index 7a067d7..53abc61 100644 --- a/titles/mai2/dx.py +++ b/titles/mai2/dx.py @@ -242,7 +242,8 @@ class Mai2DX(Mai2Base): if "userFavoriteList" in upsert and len(upsert["userFavoriteList"]) > 0: for fav in upsert["userFavoriteList"]: - await self.data.item.put_favorite(user_id, fav["kind"], fav["itemIdList"]) + kind_id = fav.get("kind", fav.get("itemKind")) # itemKind key used in BUDDiES+ + await self.data.item.put_favorite(user_id, kind_id, fav["itemIdList"]) if ( "userFriendSeasonRankingList" in upsert diff --git a/titles/mai2/schema/item.py b/titles/mai2/schema/item.py index d53ebbc..13afbe4 100644 --- a/titles/mai2/schema/item.py +++ b/titles/mai2/schema/item.py @@ -453,10 +453,10 @@ class Mai2ItemData(BaseData): self, user_id: int, kind: int, item_id_list: List[int] ) -> Optional[int]: sql = insert(favorite).values( - user=user_id, kind=kind, item_id_list=item_id_list + user=user_id, itemKind=kind, itemIdList=item_id_list ) - conflict = sql.on_duplicate_key_update(item_id_list=item_id_list) + conflict = sql.on_duplicate_key_update(itemIdList=item_id_list) result = await self.execute(conflict) if result is None: From 77aa1afaa0f14883ddce3c5f4243b4bce1e94b04 Mon Sep 17 00:00:00 2001 From: ppc Date: Mon, 16 Sep 2024 16:55:09 +0100 Subject: [PATCH 06/24] add mai2 favorite music support --- ...c91c1206dca_mai2_favorite_song_ordering.py | 24 +++++++++++++++++++ titles/mai2/base.py | 2 +- titles/mai2/dx.py | 4 ++++ titles/mai2/schema/item.py | 8 ++++--- 4 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 core/data/alembic/versions/bc91c1206dca_mai2_favorite_song_ordering.py diff --git a/core/data/alembic/versions/bc91c1206dca_mai2_favorite_song_ordering.py b/core/data/alembic/versions/bc91c1206dca_mai2_favorite_song_ordering.py new file mode 100644 index 0000000..ff4a62f --- /dev/null +++ b/core/data/alembic/versions/bc91c1206dca_mai2_favorite_song_ordering.py @@ -0,0 +1,24 @@ +"""mai2_favorite_song_ordering + +Revision ID: bc91c1206dca +Revises: 28443e2da5b8 +Create Date: 2024-09-16 14:24:56.714066 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'bc91c1206dca' +down_revision = '28443e2da5b8' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column('mai2_item_favorite_music', sa.Column('orderId', sa.Integer(nullable=True))) + + +def downgrade(): + op.drop_column('mai2_item_favorite_music', 'orderId') diff --git a/titles/mai2/base.py b/titles/mai2/base.py index bbd074f..b041028 100644 --- a/titles/mai2/base.py +++ b/titles/mai2/base.py @@ -922,7 +922,7 @@ class Mai2Base: fav_music = await self.data.item.get_fav_music(user_id) if fav_music: for fav in fav_music: - id_list.append({"orderId": 0, "id": fav["musicId"]}) + id_list.append({"orderId": fav["orderId"] or 0, "id": fav["musicId"]}) if len(id_list) >= 100: # Lazy but whatever break diff --git a/titles/mai2/dx.py b/titles/mai2/dx.py index 53abc61..0e86acd 100644 --- a/titles/mai2/dx.py +++ b/titles/mai2/dx.py @@ -245,6 +245,10 @@ class Mai2DX(Mai2Base): kind_id = fav.get("kind", fav.get("itemKind")) # itemKind key used in BUDDiES+ await self.data.item.put_favorite(user_id, kind_id, fav["itemIdList"]) + if "userFavoritemusicList" in upsert and len(upsert["userFavoritemusicList"]) > 0: + for fav in upsert["userFavoritemusicList"]: + await self.data.item.add_fav_music(user_id, fav["id"], fav["orderId"]) + if ( "userFriendSeasonRankingList" in upsert and len(upsert["userFriendSeasonRankingList"]) > 0 diff --git a/titles/mai2/schema/item.py b/titles/mai2/schema/item.py index 13afbe4..87ddca4 100644 --- a/titles/mai2/schema/item.py +++ b/titles/mai2/schema/item.py @@ -144,6 +144,7 @@ fav_music = Table( nullable=False, ), Column("musicId", Integer, nullable=False), + Column("orderId", Integer, nullable=True), UniqueConstraint("user", "musicId", name="mai2_item_favorite_music_uk"), mysql_charset="utf8mb4", ) @@ -484,13 +485,14 @@ class Mai2ItemData(BaseData): if result: return result.fetchall() - async def add_fav_music(self, user_id: int, music_id: int) -> Optional[int]: + async def add_fav_music(self, user_id: int, music_id: int, order_id: Optional[int] = None) -> Optional[int]: sql = insert(fav_music).values( user = user_id, - musicId = music_id + musicId = music_id, + orderId = order_id ) - conflict = sql.on_duplicate_key_update(musicId = music_id) + conflict = sql.on_duplicate_key_update(orderId = order_id) result = await self.execute(conflict) if result: From d8169e37cc88b2041b4a4fec2151486037459ae6 Mon Sep 17 00:00:00 2001 From: ppc Date: Mon, 16 Sep 2024 17:56:22 +0100 Subject: [PATCH 07/24] add mai2 UserIntimateApi --- .../versions/54a84103b84e_mai2_intimacy.py | 43 +++++++++++++++++++ titles/mai2/dx.py | 23 ++++++++++ titles/mai2/schema/profile.py | 37 ++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 core/data/alembic/versions/54a84103b84e_mai2_intimacy.py diff --git a/core/data/alembic/versions/54a84103b84e_mai2_intimacy.py b/core/data/alembic/versions/54a84103b84e_mai2_intimacy.py new file mode 100644 index 0000000..a180bbb --- /dev/null +++ b/core/data/alembic/versions/54a84103b84e_mai2_intimacy.py @@ -0,0 +1,43 @@ +"""mai2_intimacy + +Revision ID: 54a84103b84e +Revises: bc91c1206dca +Create Date: 2024-09-16 17:47:49.164546 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy import Column, Integer, UniqueConstraint + +# revision identifiers, used by Alembic. +revision = '54a84103b84e' +down_revision = 'bc91c1206dca' +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "mai2_user_intimate", + Column("id", Integer, primary_key=True, nullable=False), + Column("user", Integer, nullable=False), + Column("partnerId", Integer, nullable=False), + Column("intimateLevel", Integer, nullable=False), + Column("intimateCountRewarded", Integer, nullable=False), + UniqueConstraint("user", "partnerId", name="mai2_user_intimate_uk"), + mysql_charset="utf8mb4", + ) + + op.create_foreign_key( + None, + "mai2_user_intimate", + "aime_user", + ["user"], + ["id"], + ondelete="cascade", + onupdate="cascade", + ) + + +def downgrade(): + op.drop_table("mai2_user_intimate") diff --git a/titles/mai2/dx.py b/titles/mai2/dx.py index 0e86acd..a31f98c 100644 --- a/titles/mai2/dx.py +++ b/titles/mai2/dx.py @@ -264,6 +264,11 @@ class Mai2DX(Mai2Base): if "user2pPlaylog" in upsert: await self.data.score.put_playlog_2p(user_id, upsert["user2pPlaylog"]) + # added in BUDDiES+ + if "userIntimateList" in upsert and len(upsert["userIntimateList"]) > 0: + for intimate in upsert["userIntimateList"]: + await self.data.profile.put_intimacy(user_id, intimate["partnerId"], intimate["intimateLevel"], intimate["intimateCountRewarded"]) + return {"returnCode": 1, "apiName": "UpsertUserAllApi"} async def handle_get_user_data_api_request(self, data: Dict) -> Dict: @@ -713,6 +718,24 @@ class Mai2DX(Mai2Base): ret['loginId'] = ret.get('loginCount', 0) return ret + # Intimate api added in BUDDiES+ + async def handle_get_user_intimate_api_request(self, data: Dict) -> Dict: + intimate = await self.data.profile.get_intimacy(data["userId"]) + if intimate is None: + return {} + + partner_list = [{ + "partnerId": i["partnerId"], + "intimateLevel": i["intimateLevel"], + "intimateCountRewarded": i["intimateCountRewarded"] + } for i in intimate] + + return { + "userId": data["userId"], + "length": len(partner_list), + "userIntimateList": partner_list + } + # CardMaker support added in Universe async def handle_cm_get_user_preview_api_request(self, data: Dict) -> Dict: p = await self.data.profile.get_profile_detail(data["userId"], self.version) diff --git a/titles/mai2/schema/profile.py b/titles/mai2/schema/profile.py index 988dcb0..3ff85d2 100644 --- a/titles/mai2/schema/profile.py +++ b/titles/mai2/schema/profile.py @@ -513,6 +513,22 @@ rival = Table( mysql_charset="utf8mb4", ) +intimacy = Table( +"mai2_user_intimate", + metadata, + Column("id", Integer, primary_key=True, nullable=False), + Column( + "user", + ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), + nullable=False, + ), + Column("partnerId", Integer, nullable=False), + Column("intimateLevel", Integer, nullable=False), + Column("intimateCountRewarded", Integer, nullable=False), + UniqueConstraint("user", "partnerId", name="mai2_user_intimate_uk"), + mysql_charset="utf8mb4", +) + class Mai2ProfileData(BaseData): async def get_all_profile_versions(self, user_id: int) -> Optional[List[Row]]: result = await self.execute(detail.select(detail.c.user == user_id)) @@ -908,6 +924,27 @@ class Mai2ProfileData(BaseData): if not result: self.logger.error(f"Failed to remove rival {rival_id} for user {user_id}!") + async def get_intimacy(self, user_id: int) -> Optional[List[Row]]: + result = await self.execute(intimacy.select(intimacy.c.user == user_id)) + if result: + return result.fetchall() + + async def put_intimacy(self, user_id: int, partner_id: int, level: int, count_rewarded: int) -> Optional[int]: + sql = insert(intimacy).values( + user = user_id, + partnerId = partner_id, + intimateLevel = level, + intimateCountRewarded = count_rewarded + ) + + conflict = sql.on_duplicate_key_update(intimateLevel = level, intimateCountRewarded = count_rewarded) + + result = await self.execute(conflict) + if result: + return result.lastrowid + + self.logger.error(f"Failed to update intimacy for user {user_id} and partner {partner_id}!") + async def update_name(self, user_id: int, new_name: str) -> bool: sql = detail.update(detail.c.user == user_id).values( userName=new_name From 196aa601f3e461329af095b0e3b5120d30e858c3 Mon Sep 17 00:00:00 2001 From: ppc Date: Mon, 16 Sep 2024 18:01:06 +0100 Subject: [PATCH 08/24] handle GetUserMissionDataApi --- titles/mai2/buddiesplus.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/titles/mai2/buddiesplus.py b/titles/mai2/buddiesplus.py index a3784d5..e87fae6 100644 --- a/titles/mai2/buddiesplus.py +++ b/titles/mai2/buddiesplus.py @@ -47,3 +47,14 @@ class Mai2BuddiesPlus(Mai2Buddies): "userId": data["userId"], "userShopStockList": [] } + + async def handle_get_user_mission_data_api_request(self, data: Dict) -> Dict: + return { + "userId": data["userId"], + "userMissionDataList": [], + "userWeeklyData": { + "lastLoginWeek": "2024-03-21 09:00:00", + "beforeLoginWeek": "2099-12-31 00:00:00", + "friendBonusFlag": False + } + } From d85c575c61006e340722e69eae217f38a8d2b3e7 Mon Sep 17 00:00:00 2001 From: ppc Date: Wed, 18 Sep 2024 11:29:14 +0100 Subject: [PATCH 09/24] add nullcheck --- titles/mai2/dx.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/titles/mai2/dx.py b/titles/mai2/dx.py index a31f98c..66bf914 100644 --- a/titles/mai2/dx.py +++ b/titles/mai2/dx.py @@ -243,7 +243,8 @@ class Mai2DX(Mai2Base): if "userFavoriteList" in upsert and len(upsert["userFavoriteList"]) > 0: for fav in upsert["userFavoriteList"]: kind_id = fav.get("kind", fav.get("itemKind")) # itemKind key used in BUDDiES+ - await self.data.item.put_favorite(user_id, kind_id, fav["itemIdList"]) + if kind_id is not None: + await self.data.item.put_favorite(user_id, kind_id, fav["itemIdList"]) if "userFavoritemusicList" in upsert and len(upsert["userFavoritemusicList"]) > 0: for fav in upsert["userFavoritemusicList"]: From 8d04d74f523faca71b3b7552124c9f660aa041e9 Mon Sep 17 00:00:00 2001 From: ppc Date: Wed, 18 Sep 2024 17:59:24 +0100 Subject: [PATCH 10/24] migration consistency --- .../versions/bc91c1206dca_mai2_favorite_song_ordering.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/data/alembic/versions/bc91c1206dca_mai2_favorite_song_ordering.py b/core/data/alembic/versions/bc91c1206dca_mai2_favorite_song_ordering.py index ff4a62f..abf6357 100644 --- a/core/data/alembic/versions/bc91c1206dca_mai2_favorite_song_ordering.py +++ b/core/data/alembic/versions/bc91c1206dca_mai2_favorite_song_ordering.py @@ -17,7 +17,7 @@ depends_on = None def upgrade(): - op.add_column('mai2_item_favorite_music', sa.Column('orderId', sa.Integer(nullable=True))) + op.add_column('mai2_item_favorite_music', sa.Column('orderId', sa.Integer(), nullable=True)) def downgrade(): From 5c60cde14f89b9db2212f8963bcec20ade88c75e Mon Sep 17 00:00:00 2001 From: ppc Date: Thu, 19 Sep 2024 23:10:54 +0100 Subject: [PATCH 11/24] update docs --- docs/game_specific_info.md | 1 + readme.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/game_specific_info.md b/docs/game_specific_info.md index 37561fc..a0aed71 100644 --- a/docs/game_specific_info.md +++ b/docs/game_specific_info.md @@ -218,6 +218,7 @@ Presents are items given to the user when they login, with a little animation (f | SDEZ | 19 | maimai DX FESTiVAL | | SDEZ | 20 | maimai DX FESTiVAL PLUS | | SDEZ | 21 | maimai DX BUDDiES | +| SDEZ | 22 | maimai DX BUDDiES PLUS | ### Importer diff --git a/readme.md b/readme.md index c792928..0564c70 100644 --- a/readme.md +++ b/readme.md @@ -50,6 +50,7 @@ Games listed below have been tested and confirmed working. Only game versions ol + FESTiVAL + FESTiVAL PLUS + BUDDiES + + BUDDiES PLUS + O.N.G.E.K.I. + SUMMER From 3843ac6eb14b130cb8819c318c41e110344906ee Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Thu, 3 Oct 2024 19:32:17 +0000 Subject: [PATCH 12/24] mai2: calc GetGameRanking result --- titles/mai2/base.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/titles/mai2/base.py b/titles/mai2/base.py index b041028..9d85857 100644 --- a/titles/mai2/base.py +++ b/titles/mai2/base.py @@ -1,3 +1,4 @@ +import pymysql from datetime import datetime, timedelta from typing import Any, Dict, List import logging @@ -76,7 +77,40 @@ class Mai2Base: } async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: - return {"length": 0, "gameRankingList": []} + conn = pymysql.connect( + host=self.core_config.database.host, + port=self.core_config.database.port, + user=self.core_config.database.username, + password=self.core_config.database.password, + database=self.core_config.database.name, + charset='utf8mb4' + ) + try: + cursor = conn.cursor() + + query = """ + SELECT musicid AS id, COUNT(*) AS point + FROM mai2_playlog + GROUP BY musicid + ORDER BY point DESC + LIMIT 100 + """ + cursor.execute(query) + + results = cursor.fetchall() + ranking_list = [{"id": row[0], "point": row[1], "userName": ""} for row in results] + output = { + "type": 1, + "gameRankingList": ranking_list, + "gameRankingInstantList": None + } + + cursor.close() + conn.close() + return output + + except Exception as e: + return {'length': 0, 'gameRankingList': []} async def handle_get_game_tournament_info_api_request(self, data: Dict) -> Dict: # TODO: Tournament support From 58ae491a8ccd76cae5682b150b2da89bdc3b7295 Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Thu, 3 Oct 2024 19:47:36 +0000 Subject: [PATCH 13/24] add pymysql to requirements.txt --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index fe5b4ef..72d9844 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,4 +21,5 @@ starlette asyncio uvicorn alembic -python-multipart \ No newline at end of file +python-multipart +pymysql \ No newline at end of file From 033c1aa776b7874b6e534efd664f0b7010271e68 Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Fri, 11 Oct 2024 16:06:17 +0000 Subject: [PATCH 14/24] =?UTF-8?q?Update=20=E5=8D=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- titles/mai2/base.py | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/titles/mai2/base.py b/titles/mai2/base.py index 2d0b686..d498fcf 100644 --- a/titles/mai2/base.py +++ b/titles/mai2/base.py @@ -77,40 +77,33 @@ class Mai2Base: } async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: - conn = pymysql.connect( - host=self.core_config.database.host, - port=self.core_config.database.port, - user=self.core_config.database.username, - password=self.core_config.database.password, - database=self.core_config.database.name, - charset='utf8mb4' - ) try: - cursor = conn.cursor() + playlogs = await self.data.score.get_playlogs(user_id=None) + ranking_list = [] - query = """ - SELECT musicid AS id, COUNT(*) AS point - FROM mai2_playlog - GROUP BY musicid - ORDER BY point DESC - LIMIT 100 - """ - cursor.execute(query) + if not playlogs: + self.logger.warning("No playlogs found.") + return {"length": 0, "gameRankingList": []} - results = cursor.fetchall() - ranking_list = [{"id": row[0], "point": row[1], "userName": ""} for row in results] - output = { + music_count = {} + for log in playlogs: + music_id = log.musicId + music_count[music_id] = music_count.get(music_id, 0) + 1 + + sorted_music = sorted(music_count.items(), key=lambda item: item[1], reverse=True) + + for music_id, count in sorted_music[:100]: + ranking_list.append({"id": music_id, "point": count, "userName": ""}) + + return { "type": 1, "gameRankingList": ranking_list, "gameRankingInstantList": None } - cursor.close() - conn.close() - return output - except Exception as e: - return {'length': 0, 'gameRankingList': []} + self.logger.error(f"Error while getting game ranking: {e}") + return {"length": 0, "gameRankingList": []} async def handle_get_game_tournament_info_api_request(self, data: Dict) -> Dict: # TODO: Tournament support From 29f4a6a696c03170e51a3eb14b0a643f9a2386dc Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Fri, 11 Oct 2024 16:08:15 +0000 Subject: [PATCH 15/24] revert 033c1aa776b7874b6e534efd664f0b7010271e68 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit revert Update 卖 --- titles/mai2/base.py | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/titles/mai2/base.py b/titles/mai2/base.py index d498fcf..2d0b686 100644 --- a/titles/mai2/base.py +++ b/titles/mai2/base.py @@ -77,33 +77,40 @@ class Mai2Base: } async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: + conn = pymysql.connect( + host=self.core_config.database.host, + port=self.core_config.database.port, + user=self.core_config.database.username, + password=self.core_config.database.password, + database=self.core_config.database.name, + charset='utf8mb4' + ) try: - playlogs = await self.data.score.get_playlogs(user_id=None) - ranking_list = [] + cursor = conn.cursor() - if not playlogs: - self.logger.warning("No playlogs found.") - return {"length": 0, "gameRankingList": []} + query = """ + SELECT musicid AS id, COUNT(*) AS point + FROM mai2_playlog + GROUP BY musicid + ORDER BY point DESC + LIMIT 100 + """ + cursor.execute(query) - music_count = {} - for log in playlogs: - music_id = log.musicId - music_count[music_id] = music_count.get(music_id, 0) + 1 - - sorted_music = sorted(music_count.items(), key=lambda item: item[1], reverse=True) - - for music_id, count in sorted_music[:100]: - ranking_list.append({"id": music_id, "point": count, "userName": ""}) - - return { + results = cursor.fetchall() + ranking_list = [{"id": row[0], "point": row[1], "userName": ""} for row in results] + output = { "type": 1, "gameRankingList": ranking_list, "gameRankingInstantList": None } + cursor.close() + conn.close() + return output + except Exception as e: - self.logger.error(f"Error while getting game ranking: {e}") - return {"length": 0, "gameRankingList": []} + return {'length': 0, 'gameRankingList': []} async def handle_get_game_tournament_info_api_request(self, data: Dict) -> Dict: # TODO: Tournament support From 398fa9059d3f14900721678934ad546a76061481 Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Fri, 11 Oct 2024 16:09:53 +0000 Subject: [PATCH 16/24] Update mai2/base.py using the ORM --- titles/mai2/base.py | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/titles/mai2/base.py b/titles/mai2/base.py index 2d0b686..d498fcf 100644 --- a/titles/mai2/base.py +++ b/titles/mai2/base.py @@ -77,40 +77,33 @@ class Mai2Base: } async def handle_get_game_ranking_api_request(self, data: Dict) -> Dict: - conn = pymysql.connect( - host=self.core_config.database.host, - port=self.core_config.database.port, - user=self.core_config.database.username, - password=self.core_config.database.password, - database=self.core_config.database.name, - charset='utf8mb4' - ) try: - cursor = conn.cursor() + playlogs = await self.data.score.get_playlogs(user_id=None) + ranking_list = [] - query = """ - SELECT musicid AS id, COUNT(*) AS point - FROM mai2_playlog - GROUP BY musicid - ORDER BY point DESC - LIMIT 100 - """ - cursor.execute(query) + if not playlogs: + self.logger.warning("No playlogs found.") + return {"length": 0, "gameRankingList": []} - results = cursor.fetchall() - ranking_list = [{"id": row[0], "point": row[1], "userName": ""} for row in results] - output = { + music_count = {} + for log in playlogs: + music_id = log.musicId + music_count[music_id] = music_count.get(music_id, 0) + 1 + + sorted_music = sorted(music_count.items(), key=lambda item: item[1], reverse=True) + + for music_id, count in sorted_music[:100]: + ranking_list.append({"id": music_id, "point": count, "userName": ""}) + + return { "type": 1, "gameRankingList": ranking_list, "gameRankingInstantList": None } - cursor.close() - conn.close() - return output - except Exception as e: - return {'length': 0, 'gameRankingList': []} + self.logger.error(f"Error while getting game ranking: {e}") + return {"length": 0, "gameRankingList": []} async def handle_get_game_tournament_info_api_request(self, data: Dict) -> Dict: # TODO: Tournament support From a673d9dabd4cde016a9239ec45b5ffab37246364 Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Fri, 11 Oct 2024 16:12:53 +0000 Subject: [PATCH 17/24] Delete unused dependency --- titles/mai2/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/titles/mai2/base.py b/titles/mai2/base.py index d498fcf..cde1e1e 100644 --- a/titles/mai2/base.py +++ b/titles/mai2/base.py @@ -1,4 +1,3 @@ -import pymysql from datetime import datetime, timedelta from typing import Any, Dict, List import logging From 598e4aad76dbc915fae47ff2c61b469732c20c9b Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Fri, 11 Oct 2024 16:16:40 +0000 Subject: [PATCH 18/24] Update mai2/schema/score.py to support new handle_get_game_ranking --- titles/mai2/schema/score.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/titles/mai2/schema/score.py b/titles/mai2/schema/score.py index f62466a..e376216 100644 --- a/titles/mai2/schema/score.py +++ b/titles/mai2/schema/score.py @@ -396,8 +396,11 @@ class Mai2ScoreData(BaseData): return result.fetchall() async def get_playlogs(self, user_id: int, idx: int = 0, limit: int = 0) -> Optional[List[Row]]: - sql = playlog.select(playlog.c.user == user_id) - + if user_id is not None: + sql = playlog.select(playlog.c.user == user_id) + else: + sql = playlog.select() + if limit: sql = sql.limit(limit) if idx: From b6e7e0973b0db16bc0ac1d77657934c7f34f245c Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Fri, 11 Oct 2024 16:19:07 +0000 Subject: [PATCH 19/24] Delete unused dependency --- requirements.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 72d9844..fe5b4ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,5 +21,4 @@ starlette asyncio uvicorn alembic -python-multipart -pymysql \ No newline at end of file +python-multipart \ No newline at end of file From 221517e3108f8c84366e1c34182823a0ac79f9e4 Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Wed, 30 Oct 2024 12:37:18 +0800 Subject: [PATCH 20/24] TODO: GetUserScoreRankingApi --- titles/mai2/base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/titles/mai2/base.py b/titles/mai2/base.py index cde1e1e..1c9d91c 100644 --- a/titles/mai2/base.py +++ b/titles/mai2/base.py @@ -963,3 +963,6 @@ class Mai2Base: userRecommendSelectionMusicIdList: list[int] """ return {"userId": data["userId"], "userRecommendSelectionMusicIdList": []} + async def handle_get_user_score_ranking_api_request(self, data: Dict) ->Dict: + + return {"userId": data["userId"], "userScoreRanking": []} \ No newline at end of file From aa7ae6cb514cde926b58bd95e7a7e410793c79b9 Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Wed, 6 Nov 2024 02:38:18 +0800 Subject: [PATCH 21/24] Formatted log print --- titles/mai2/frontend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titles/mai2/frontend.py b/titles/mai2/frontend.py index f1f961a..3e08dae 100644 --- a/titles/mai2/frontend.py +++ b/titles/mai2/frontend.py @@ -383,7 +383,7 @@ class Mai2Frontend(FE_Base): return Response(status_code=404) if path.exists(f"{out_folder}"): - print("path exists") + self.logger.debug(f"Photo Path Exist.") max_idx = 0 p = ImageFile.Parser() for _, _, files in walk("out_folder"): From f272e97eae18b89a5e82a740056894b96c0273a4 Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Wed, 6 Nov 2024 02:44:07 +0800 Subject: [PATCH 22/24] Formatted log print Change log level --- titles/mai2/frontend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/titles/mai2/frontend.py b/titles/mai2/frontend.py index 3e08dae..436d488 100644 --- a/titles/mai2/frontend.py +++ b/titles/mai2/frontend.py @@ -383,7 +383,7 @@ class Mai2Frontend(FE_Base): return Response(status_code=404) if path.exists(f"{out_folder}"): - self.logger.debug(f"Photo Path Exist.") + self.logger.info(f"Photo Path Exist.") max_idx = 0 p = ImageFile.Parser() for _, _, files in walk("out_folder"): @@ -417,7 +417,7 @@ class Mai2Frontend(FE_Base): shutil.rmtree(out_folder) if path.exists(f"{out_folder}.jpeg"): - print(f"{out_folder}.jpeg exists") + self.logger.info(f"{out_folder}.jpeg exists") return FileResponse(f"{out_folder}.jpeg") return Response(status_code=404) From 8a6250bebd4ba8ce90ae7092b5d7bee58399161f Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Mon, 11 Nov 2024 21:11:33 +0800 Subject: [PATCH 23/24] Formatted log print Change log level --- titles/mai2/frontend.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/titles/mai2/frontend.py b/titles/mai2/frontend.py index 436d488..b39a37e 100644 --- a/titles/mai2/frontend.py +++ b/titles/mai2/frontend.py @@ -389,7 +389,7 @@ class Mai2Frontend(FE_Base): for _, _, files in walk("out_folder"): if not files: break - + matcher = re.match("^(\d+)_(\d+)$", files[0]) if not matcher: break @@ -410,6 +410,7 @@ class Mai2Frontend(FE_Base): try: im = p.close() im.save(f"{out_folder}.jpeg") + self.logger.info(f"{out_folder}.jpeg generated.") except Exception as e: self.logger.error(f"{photo_id} failed PIL validation! - {e}") From f4dff9b4c1bd09b536b9e2e3b2c638a2c973c0de Mon Sep 17 00:00:00 2001 From: SoulGateKey Date: Mon, 11 Nov 2024 21:16:19 +0800 Subject: [PATCH 24/24] fix: mai2 photos cant be merged --- titles/mai2/frontend.py | 130 ++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/titles/mai2/frontend.py b/titles/mai2/frontend.py index b39a37e..9667eca 100644 --- a/titles/mai2/frontend.py +++ b/titles/mai2/frontend.py @@ -55,7 +55,7 @@ class Mai2Frontend(FE_Base): usr_sesh = self.validate_session(request) if not usr_sesh: usr_sesh = UserSession() - + incoming_ver = usr_sesh.maimai_version if usr_sesh.user_id > 0: @@ -103,10 +103,10 @@ class Mai2Frontend(FE_Base): if not path_index or int(path_index) < 1: index = 0 else: - index = int(path_index) - 1 # 0 and 1 are 1st page + index = int(path_index) - 1 # 0 and 1 are 1st page user_id = usr_sesh.user_id playlog_count = await self.data.score.get_user_playlogs_count(user_id) - if playlog_count < index * 20 : + if playlog_count < index * 20: return Response(template.render( title=f"{self.core_config.server.name} | {self.nav_name}", game_list=self.environment.globals["game_list"], @@ -116,17 +116,17 @@ class Mai2Frontend(FE_Base): playlog = await self.data.score.get_playlogs(user_id, index, 20) playlog_with_title = [] for record in playlog: - music_chart = await self.data.static.get_music_chart(usr_sesh.maimai_version, record.musicId, record.level) + music_chart = await self.data.static.get_music_chart(usr_sesh.maimai_version, record.musicId, record.level) if music_chart: - difficultyNum=music_chart.chartId - difficulty=music_chart.difficulty - artist=music_chart.artist - title=music_chart.title + difficultyNum = music_chart.chartId + difficulty = music_chart.difficulty + artist = music_chart.artist + title = music_chart.title else: - difficultyNum=0 - difficulty=0 - artist="unknown" - title="musicid: " + str(record.musicId) + difficultyNum = 0 + difficulty = 0 + artist = "unknown" + title = "musicid: " + str(record.musicId) playlog_with_title.append({ "raw": record, "title": title, @@ -156,29 +156,29 @@ class Mai2Frontend(FE_Base): if usr_sesh.user_id > 0: if usr_sesh.maimai_version < 0: return RedirectResponse("/game/mai2/", 303) - + photos = await self.data.profile.get_user_photos_by_user(usr_sesh.user_id) photos_fixed = [] for photo in photos: if datetime.now().timestamp() > (photo['when_upload'] + timedelta(days=7)).timestamp(): await self.data.profile.delete_user_photo_by_id(photo['id']) - + if path.exists(f"{self.game_cfg.uploads.photos_dir}/{photo['id']}.jpeg"): remove(f"{self.game_cfg.uploads.photos_dir}/{photo['id']}.jpeg") if path.exists(f"{self.game_cfg.uploads.photos_dir}/{photo['id']}"): shutil.rmtree(f"{self.game_cfg.uploads.photos_dir}/{photo['id']}") - + continue - + photos_fixed.append({ "id": photo['id'], "playlog_num": photo['playlog_num'], "track_num": photo['track_num'], "when_upload": photo['when_upload'], }) - + return Response(template.render( title=f"{self.core_config.server.name} | {self.nav_name}", game_list=self.environment.globals["game_list"], @@ -195,7 +195,7 @@ class Mai2Frontend(FE_Base): return RedirectResponse("/gate/", 303) form_data = await request.form() - new_name: str = form_data.get("new_name") + new_name: str = form_data.get("new_name") new_name_full = "" if not new_name: @@ -204,7 +204,7 @@ class Mai2Frontend(FE_Base): if len(new_name) > 8: return RedirectResponse("/gate/?e=8", 303) - for x in new_name: # FIXME: This will let some invalid characters through atm + for x in new_name: # FIXME: This will let some invalid characters through atm o = ord(x) try: if o == 0x20: @@ -235,13 +235,13 @@ class Mai2Frontend(FE_Base): resp = RedirectResponse("/game/mai2/events/", 303) else: resp = RedirectResponse("/game/mai2/", 303) - + if usr_sesh.user_id > 0: form_data = await request.form() maimai_version = form_data.get("version") self.logger.info(f"version change to: {maimai_version}") - if(maimai_version.isdigit()): - usr_sesh.maimai_version=int(maimai_version) + if (maimai_version.isdigit()): + usr_sesh.maimai_version = int(maimai_version) encoded_sesh = self.encode_session(usr_sesh) self.logger.debug(f"Created session with JWT {encoded_sesh}") resp.set_cookie("ARTEMIS_SESH", encoded_sesh) @@ -253,20 +253,20 @@ class Mai2Frontend(FE_Base): usr_sesh = self.validate_session(request) if not usr_sesh: return RedirectResponse("/gate/", 303) - + if not self.test_perm(usr_sesh.permissions, PermissionOffset.SYSADMIN): return RedirectResponse("/game/mai2/", 303) - + template = self.environment.get_template( "titles/mai2/templates/events/mai2_events.jinja" ) - + incoming_ver = usr_sesh.maimai_version evts = [] - + if incoming_ver < 0: usr_sesh.maimai_version = Mai2Constants.VER_MAIMAI_DX - + event_list = await self.data.static.get_game_events(usr_sesh.maimai_version) self.logger.info(f"Get events for v{usr_sesh.maimai_version}") @@ -280,88 +280,88 @@ class Mai2Frontend(FE_Base): "startDate": event['startDate'].strftime("%x %X"), "enabled": "true" if event['enabled'] else "false", }) - + resp = Response(template.render( - title=f"{self.core_config.server.name} | {self.nav_name} Events", - game_list=self.environment.globals["game_list"], - sesh=vars(usr_sesh), - version_list=Mai2Constants.VERSION_STRING, - events=evts - ), media_type="text/html; charset=utf-8") - + title=f"{self.core_config.server.name} | {self.nav_name} Events", + game_list=self.environment.globals["game_list"], + sesh=vars(usr_sesh), + version_list=Mai2Constants.VERSION_STRING, + events=evts + ), media_type="text/html; charset=utf-8") + if incoming_ver < 0: encoded_sesh = self.encode_session(usr_sesh) resp.delete_cookie("ARTEMIS_SESH") resp.set_cookie("ARTEMIS_SESH", encoded_sesh) - + return resp async def render_event_edit(self, request: Request) -> Response: usr_sesh = self.validate_session(request) if not usr_sesh: return RedirectResponse("/gate/", 303) - + if not self.test_perm(usr_sesh.permissions, PermissionOffset.SYSADMIN): return RedirectResponse("/game/mai2/", 303) - + template = self.environment.get_template( "titles/mai2/templates/events/mai2_event_edit.jinja" ) - + evt_id = request.path_params.get("event_id") - + event_id = await self.data.static.get_event_by_id(evt_id) if not event_id: return RedirectResponse("/game/mai2/events/", 303) - + return Response(template.render( - title=f"{self.core_config.server.name} | {self.nav_name} Edit Event {evt_id}", - game_list=self.environment.globals["game_list"], - sesh=vars(usr_sesh), - user_id=usr_sesh.user_id, - version_list=Mai2Constants.VERSION_STRING, - cur_version=usr_sesh.maimai_version, - event=event_id._asdict() - ), media_type="text/html; charset=utf-8") + title=f"{self.core_config.server.name} | {self.nav_name} Edit Event {evt_id}", + game_list=self.environment.globals["game_list"], + sesh=vars(usr_sesh), + user_id=usr_sesh.user_id, + version_list=Mai2Constants.VERSION_STRING, + cur_version=usr_sesh.maimai_version, + event=event_id._asdict() + ), media_type="text/html; charset=utf-8") async def update_event(self, request: Request) -> RedirectResponse: usr_sesh = self.validate_session(request) if not usr_sesh: return RedirectResponse("/gate/", 303) - + if not self.test_perm(usr_sesh.permissions, PermissionOffset.SYSADMIN): return RedirectResponse("/game/mai2/", 303) - + form_data = await request.form() print(form_data) event_id: int = form_data.get("evtId", None) - new_enabled: bool = bool(form_data.get("evtEnabled", False)) + new_enabled: bool = bool(form_data.get("evtEnabled", False)) try: new_start_date: datetime = datetime.strptime(form_data.get("evtStart", None), "%Y-%m-%dT%H:%M:%S") except: new_start_date = None - + print(f"{event_id} {new_enabled} {new_start_date}") if event_id is None or new_start_date is None: return RedirectResponse("/game/mai2/events/?e=4", 303) await self.data.static.update_event_by_id(int(event_id), new_enabled, new_start_date) - + return RedirectResponse("/game/mai2/events/?s=1", 303) async def get_photo(self, request: Request) -> RedirectResponse: usr_sesh = self.validate_session(request) if not usr_sesh: return RedirectResponse("/gate/", 303) - + photo_jpeg = request.path_params.get("photo_id", None) if not photo_jpeg: return Response(status_code=400) - + matcher = re.match(r"^([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}).jpeg$", photo_jpeg) if not matcher: return Response(status_code=400) - + photo_id = matcher.groups()[0] photo_info = await self.data.profile.get_user_photo_by_id(photo_id) if not photo_info: @@ -379,18 +379,18 @@ class Mai2Frontend(FE_Base): if path.exists(f"{out_folder}"): shutil.rmtree(out_folder) - + return Response(status_code=404) if path.exists(f"{out_folder}"): self.logger.info(f"Photo Path Exist.") max_idx = 0 p = ImageFile.Parser() - for _, _, files in walk("out_folder"): + for _, _, files in walk(f"{out_folder}"): if not files: break - matcher = re.match("^(\d+)_(\d+)$", files[0]) + matcher = re.match(r"^(\d+)_(\d+)\.bin$", files[0]) if not matcher: break @@ -400,18 +400,18 @@ class Mai2Frontend(FE_Base): self.logger.error(f"Expected {max_idx + 1} files, found {len(files)}") max_idx = 0 break - + if max_idx == 0: return Response(status_code=500) - + for i in range(max_idx + 1): - with open(f"{out_folder}/{i}_{max_idx}", "rb") as f: + with open(f"{out_folder}/{i}_{max_idx}.bin", "rb") as f: p.feed(f.read()) try: im = p.close() im.save(f"{out_folder}.jpeg") self.logger.info(f"{out_folder}.jpeg generated.") - + except Exception as e: self.logger.error(f"{photo_id} failed PIL validation! - {e}") @@ -420,5 +420,5 @@ class Mai2Frontend(FE_Base): if path.exists(f"{out_folder}.jpeg"): self.logger.info(f"{out_folder}.jpeg exists") return FileResponse(f"{out_folder}.jpeg") - + return Response(status_code=404)