diff --git a/titles/mai2/buddiesplus.py b/titles/mai2/buddiesplus.py index e87fae6..6ed0025 100644 --- a/titles/mai2/buddiesplus.py +++ b/titles/mai2/buddiesplus.py @@ -58,3 +58,62 @@ class Mai2BuddiesPlus(Mai2Buddies): "friendBonusFlag": False } } + + async def handle_get_user_friend_check_api_request(self, data: Dict) -> Dict: + user1rivalList = await self.data.profile.get_rivals(data["userId1"]) + user2rivalList = await self.data.profile.get_rivals(data["userId2"]) + + is_user2_in_user1_rivals = any(rival["rival"] == data["userId2"] for rival in user1rivalList) + is_user1_in_user2_rivals = any(rival["rival"] == data["userId1"] for rival in user2rivalList) + + if is_user2_in_user1_rivals and is_user1_in_user2_rivals: + return {"returnCode": 0} + else: + return {"returnCode": 1} + + async def handle_user_friend_regist_api_request(self, data: Dict) -> Dict: + user1rivalList = await self.data.profile.get_rivals(data["userId1"]) or [] + user2rivalList = await self.data.profile.get_rivals(data["userId2"]) or [] + + is_user2_in_user1_rivals = any(row.rival == data["userId2"] for row in user1rivalList) + is_user1_in_user2_rivals = any(row.rival == data["userId1"] for row in user2rivalList) + user1_show_count = sum(1 for row in user1rivalList if row.show is True) + user2_show_count = sum(1 for row in user2rivalList if row.show is True) + + # initialize returnCode + returnCode1 = 2 + returnCode2 = 2 + + # Case1 no rival + if not is_user2_in_user1_rivals and not is_user1_in_user2_rivals: + if user1_show_count >= 3 and user2_show_count >= 3: + returnCode1, returnCode2 = 1, 1 + elif user1_show_count >= 3: + returnCode1, returnCode2 = 1, 2 + elif user2_show_count >= 3: + returnCode1, returnCode2 = 2, 1 + + # Case2 has single rival + elif is_user2_in_user1_rivals != is_user1_in_user2_rivals: + if user1_show_count >= 3 and user2_show_count >= 3: + returnCode1, returnCode2 = 1, 1 + elif user1_show_count >= 3: + returnCode1, returnCode2 = 1, 2 + elif user2_show_count >= 3: + returnCode1, returnCode2 = 2, 1 + + # execute add_rival and show_rival + if not is_user2_in_user1_rivals: + await self.data.profile.add_rival(data["userId1"], data["userId2"]) + if returnCode1 == 2 and user1_show_count < 3: + await self.data.profile.set_rival_shown(data["userId1"], data["userId2"], True) + + if not is_user1_in_user2_rivals: + await self.data.profile.add_rival(data["userId2"], data["userId1"]) + if returnCode2 == 2 and user2_show_count < 3: + await self.data.profile.set_rival_shown(data["userId2"], data["userId1"], True) + + return { + "returnCode1": returnCode1, + "returnCode2": returnCode2 + } \ No newline at end of file diff --git a/titles/mai2/dx.py b/titles/mai2/dx.py index 9b8b547..498aabb 100644 --- a/titles/mai2/dx.py +++ b/titles/mai2/dx.py @@ -258,9 +258,9 @@ class Mai2DX(Mai2Base): 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"]: - await self.data.item.add_fav_music(user_id, fav["id"], fav["orderId"]) + # added in BUDDiES+ + if "isNewFavoritemusicList" in upsert and upsert["isNewFavoritemusicList"] != "" and "userFavoritemusicList" in upsert: + await self.data.item.put_fav_music(user_id, ((fav["id"], fav["orderId"]) for fav in upsert["userFavoritemusicList"])) if ( "userFriendSeasonRankingList" in upsert diff --git a/titles/mai2/frontend.py b/titles/mai2/frontend.py index c760e13..aa030b8 100644 --- a/titles/mai2/frontend.py +++ b/titles/mai2/frontend.py @@ -46,6 +46,9 @@ class Mai2Frontend(FE_Base): Route("/update.name", self.update_name, methods=['POST']), Route("/version.change", self.version_change, methods=['POST']), Route("/photo/{photo_id}", self.get_photo, methods=['GET']), + Route("/rival.add", self.rival_POST, methods=['POST']), + Route("/rival.delete", self.rival_POST, methods=['POST']), + Route("/rival.show", self.rival_POST, methods=['POST']), ] async def render_GET(self, request: Request) -> bytes: @@ -61,11 +64,22 @@ class Mai2Frontend(FE_Base): if usr_sesh.user_id > 0: versions = await self.data.profile.get_all_profile_versions(usr_sesh.user_id) profile = [] + new_rival_list = [] if versions: # maimai_version is -1 means it is not initialized yet, select a default version from existing. if incoming_ver < 0: usr_sesh.maimai_version = versions[0]['version'] profile = await self.data.profile.get_profile_detail(usr_sesh.user_id, usr_sesh.maimai_version) + rival_list = await self.data.profile.get_rivals(usr_sesh.user_id) + + for rival in rival_list: + rivalid = rival["rival"] + rivalShow = rival["show"] + rivalprofile = await self.data.profile.get_profile_detail(rivalid, usr_sesh.maimai_version) + rivalName = rivalprofile["userName"] if rivalprofile else "UnknownName" + rivalRating = rivalprofile["playerRating"] if rivalprofile else 0 + new_rival = (rivalName, rivalRating, rivalid, rivalShow) + new_rival_list.append(new_rival) versions = [x['version'] for x in versions] resp = Response(template.render( @@ -76,7 +90,8 @@ class Mai2Frontend(FE_Base): profile=profile, version_list=Mai2Constants.VERSION_STRING, versions=versions, - cur_version=usr_sesh.maimai_version + cur_version=usr_sesh.maimai_version, + rival_list=new_rival_list ), media_type="text/html; charset=utf-8") if incoming_ver < 0: @@ -420,3 +435,28 @@ class Mai2Frontend(FE_Base): return FileResponse(f"{out_folder}.jpeg") return Response(status_code=404) + async def rival_POST(self, request: Request): + uri = request.url.path + frm = await request.form() + usr_sesh = self.validate_session(request) + if not usr_sesh: + usr_sesh = UserSession() + + if usr_sesh.user_id > 0: + if uri == "/game/mai2/rival.add": + rival_id = frm.get("rivalUserId") + await self.data.profile.add_rival(usr_sesh.user_id, rival_id) + # self.logger.info(f"{usr_sesh.user_id} added a rival") + return RedirectResponse("/game/mai2/", 303) + + elif uri == "/game/mai2/rival.delete": + rival_id = frm.get("rivalUserId") + await self.data.profile.remove_rival(usr_sesh.user_id, rival_id) + # self.logger.info(f"{response}") + return RedirectResponse("/game/mai2/", 303) + + elif uri == "/game/mai2/rival.show": + rival_id = frm.get("rivalUserId") + show = frm.get("showRival", "false") == "true" + await self.data.profile.set_rival_shown(usr_sesh.user_id, rival_id, show) + return RedirectResponse("/game/mai2/", 303) \ No newline at end of file diff --git a/titles/mai2/schema/item.py b/titles/mai2/schema/item.py index 8639ae5..bb35756 100644 --- a/titles/mai2/schema/item.py +++ b/titles/mai2/schema/item.py @@ -1,7 +1,8 @@ +from collections.abc import Iterable from datetime import datetime from typing import Dict, List, Optional -from sqlalchemy import Column, Table, UniqueConstraint, and_, or_ +from sqlalchemy import Column, Table, UniqueConstraint, and_, or_, not_ from sqlalchemy.dialects.mysql import insert from sqlalchemy.engine import Row from sqlalchemy.schema import ForeignKey @@ -550,25 +551,36 @@ class Mai2ItemData(BaseData): if result: return result.fetchall() - 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, - orderId = order_id - ) + async def put_fav_music(self, user_id: int, fav_list: Iterable[tuple[int, Optional[int]]]) -> Optional[int]: + row_count = 0 + processed_music_ids = [] + + for music_id, order_id in fav_list: + sql = insert(fav_music).values( + user = user_id, + musicId = music_id, + orderId = order_id + ) + + conflict = sql.on_duplicate_key_update(orderId = order_id) + result = await self.execute(conflict) + + processed_music_ids.append(music_id) + + if not result: + self.logger.error(f"Failed to add music {music_id} as favorite for user {user_id}!") + continue + + row_count += result.rowcount + + clear_stale_entries_stmt = fav_music.delete(and_(fav_music.c.user == user_id, not_(fav_music.c.musicId.in_(processed_music_ids)))) + result = await self.execute(clear_stale_entries_stmt) + + if result is None: + self.logger.error(f"Failed to clear stale favorite music entries for user {user_id}!") + return None - conflict = sql.on_duplicate_key_update(orderId = order_id) - - result = await self.execute(conflict) - if result: - return result.lastrowid - - self.logger.error(f"Failed to add music {music_id} as favorite for user {user_id}!") - - async def remove_fav_music(self, user_id: int, music_id: int) -> None: - result = await self.execute(fav_music.delete(and_(fav_music.c.user == user_id, fav_music.c.musicId == music_id))) - if not result: - self.logger.error(f"Failed to remove music {music_id} as favorite for user {user_id}!") + return row_count + result.rowcount async def put_card( self, diff --git a/titles/mai2/schema/static.py b/titles/mai2/schema/static.py index 29e020e..a12e0fd 100644 --- a/titles/mai2/schema/static.py +++ b/titles/mai2/schema/static.py @@ -254,7 +254,7 @@ class Mai2StaticData(BaseData): async def put_card(self, version: int, card_id: int, card_name: str, opt_id: int = None, **card_data) -> int: sql = insert(cards).values( - version=version, cardId=card_id, cardName=card_name, opt=coalesce(cards.c.opt, opt_id) **card_data + version=version, cardId=card_id, cardName=card_name, opt=coalesce(cards.c.opt, opt_id), **card_data ) conflict = sql.on_duplicate_key_update(opt=coalesce(cards.c.opt, opt_id), **card_data) diff --git a/titles/mai2/templates/mai2_index.jinja b/titles/mai2/templates/mai2_index.jinja index 6490fdc..50ebb72 100644 --- a/titles/mai2/templates/mai2_index.jinja +++ b/titles/mai2/templates/mai2_index.jinja @@ -17,6 +17,10 @@ +
Id | +Name | +Rating | +Show | ++ |
---|---|---|---|---|
{{ rival.2 }} | +{{ rival.0 }} | +{{ rival.1 }} | +
+
+
+
+ |
+ + + | +