from datetime import datetime, timedelta from typing import Dict from core.config import CoreConfig from titles.wacca.config import WaccaConfig from titles.wacca.const import WaccaConstants from titles.wacca.handlers import * from titles.wacca.s import WaccaS class WaccaLily(WaccaS): def __init__(self, cfg: CoreConfig, game_cfg: WaccaConfig) -> None: super().__init__(cfg, game_cfg) self.version = WaccaConstants.VER_WACCA_LILY self.season = 2 self.OPTIONS_DEFAULTS["set_nav_id"] = 210002 self.allowed_stages = [ (2014, 14), (2013, 13), (2012, 12), (2011, 11), (2010, 10), (2009, 9), (2008, 8), (2007, 7), (2006, 6), (2005, 5), (2004, 4), (2003, 3), (2002, 2), (2001, 1), (210001, 0), (210002, 0), (210003, 0), ] async def handle_advertise_GetNews_request(self, data: Dict) -> Dict: resp = GetNewsResponseV3() return resp.make() async def handle_user_status_create_request(self, data: Dict) -> Dict: req = UserStatusCreateRequest(data) ret = await super().handle_user_status_create_request(data) new_user = await self.data.profile.get_profile(aime_id=req.aimeId) if new_user is None: return BaseResponse().make() await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["user_plate"], 211001 ) # Added lily await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["note_sound"], 205005 ) # Added lily await self.data.item.put_item( req.aimeId, WaccaConstants.ITEM_TYPES["navigator"], 210002 ) # Lily, Added Lily return ret async def handle_user_status_get_request(self, data: Dict) -> Dict: req = UserStatusGetRequest(data) resp = UserStatusGetV2Response() profile = await self.data.profile.get_profile(aime_id=req.aimeId) if profile is None: self.logger.info(f"No user exists for aime id {req.aimeId}") resp.profileStatus = ProfileStatus.ProfileRegister return resp.make() opts = await self.data.profile.get_options(req.aimeId) self.logger.info(f"User preview for {req.aimeId} from {req.chipId}") if profile["last_game_ver"] is None: resp.lastGameVersion = ShortVersion(str(req.appVersion)) else: resp.lastGameVersion = ShortVersion(profile["last_game_ver"]) resp.userStatus.userId = profile["id"] resp.userStatus.username = profile["username"] resp.userStatus.xp = profile["xp"] resp.userStatus.danLevel = profile["dan_level"] resp.userStatus.danType = profile["dan_type"] resp.userStatus.wp = profile["wp"] resp.userStatus.useCount = profile["login_count"] resp.userStatus.loginDays = profile["login_count_days"] resp.userStatus.loginConsecutiveDays = profile["login_count_days_consec"] resp.userStatus.loginsToday = profile["login_count_today"] resp.userStatus.rating = profile["rating"] set_title_id = await self.data.profile.get_options( WaccaConstants.OPTIONS["set_title_id"], profile["user"] ) if set_title_id is None: set_title_id = self.OPTIONS_DEFAULTS["set_title_id"] resp.setTitleId = set_title_id set_icon_id = await self.data.profile.get_options( WaccaConstants.OPTIONS["set_title_id"], profile["user"] ) if set_icon_id is None: set_icon_id = self.OPTIONS_DEFAULTS["set_icon_id"] resp.setIconId = set_icon_id if profile["last_login_date"].timestamp() < int( datetime.now() .replace(hour=0, minute=0, second=0, microsecond=0) .timestamp() ): resp.userStatus.loginsToday = 0 if profile["last_login_date"].timestamp() < int( ( datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=1) ).timestamp() ): resp.userStatus.loginConsecutiveDays = 0 if req.appVersion > resp.lastGameVersion: resp.versionStatus = PlayVersionStatus.VersionUpgrade elif req.appVersion < resp.lastGameVersion: resp.versionStatus = PlayVersionStatus.VersionTooNew if profile["vip_expire_time"] is not None: resp.userStatus.vipExpireTime = int(profile["vip_expire_time"].timestamp()) if profile["always_vip"] or self.game_config.mods.always_vip: resp.userStatus.vipExpireTime = int( (datetime.now() + timedelta(days=30)).timestamp() ) if self.game_config.mods.infinite_wp: resp.userStatus.wp = 999999 for opt in opts: resp.options.append(UserOption(opt["opt_id"], opt["value"])) return resp.make() async def handle_user_status_login_request(self, data: Dict) -> Dict: req = UserStatusLoginRequest(data) resp = UserStatusLoginResponseV2() is_consec_day = True if req.userId == 0: self.logger.info(f"Guest login on {req.chipId}") resp.lastLoginDate = 0 else: profile = await self.data.profile.get_profile(req.userId) if profile is None: self.logger.warning( f"Unknown user id {req.userId} attempted login from {req.chipId}" ) return resp.make() self.logger.info(f"User {req.userId} login on {req.chipId}") last_login_time = int(profile["last_login_date"].timestamp()) resp.lastLoginDate = last_login_time midnight_today_ts = int( datetime.now() .replace(hour=0, minute=0, second=0, microsecond=0) .timestamp() ) # If somebodies login timestamp < midnight of current day, then they are logging in for the first time today if last_login_time < midnight_today_ts: resp.firstLoginDaily = True # If the difference between midnight today and their last login is greater then 1 day (86400 seconds) they've broken their streak if midnight_today_ts - last_login_time > 86400: is_consec_day = False await self.data.profile.session_login( req.userId, resp.firstLoginDaily, is_consec_day ) resp.vipInfo.pageYear = datetime.now().year resp.vipInfo.pageMonth = datetime.now().month resp.vipInfo.pageDay = datetime.now().day resp.vipInfo.numItem = 1 return resp.make() async def handle_user_status_getDetail_request(self, data: Dict) -> Dict: req = UserStatusGetDetailRequest(data) if req.appVersion.minor >= 53: resp = UserStatusGetDetailResponseV3() else: resp = UserStatusGetDetailResponseV2() profile = await self.data.profile.get_profile(req.userId) if profile is None: self.logger.warning(f"Unknown profile {req.userId}") return resp.make() self.logger.info(f"Get detail for profile {req.userId}") user_id = profile["user"] profile_scores = await self.data.score.get_best_scores(user_id) profile_items = await self.data.item.get_items(user_id) profile_song_unlocks = await self.data.item.get_song_unlocks(user_id) profile_options = await self.data.profile.get_options(user_id) profile_favorites = await self.data.profile.get_favorite_songs(user_id) profile_gates = await self.data.profile.get_gates(user_id) profile_trophies = await self.data.item.get_trophies(user_id) profile_tickets = await self.data.item.get_tickets(user_id) if profile["vip_expire_time"] is None: resp.userStatus.vipExpireTime = 0 else: resp.userStatus.vipExpireTime = int(profile["vip_expire_time"].timestamp()) if profile["always_vip"] or self.game_config.mods.always_vip: resp.userStatus.vipExpireTime = int( (self.srvtime + timedelta(days=31)).timestamp() ) resp.songUpdateTime = int(profile["last_login_date"].timestamp()) resp.lastSongInfo = LastSongDetail( profile["last_song_id"], profile["last_song_difficulty"], profile["last_folder_order"], profile["last_folder_id"], profile["last_song_order"], ) resp.songPlayStatus = [resp.lastSongInfo.lastSongId, 1] resp.userStatus.userId = profile["id"] resp.userStatus.username = profile["username"] resp.userStatus.xp = profile["xp"] resp.userStatus.danLevel = profile["dan_level"] resp.userStatus.danType = profile["dan_type"] resp.userStatus.wp = profile["wp"] resp.userStatus.useCount = profile["login_count"] resp.userStatus.loginDays = profile["login_count_days"] resp.userStatus.loginConsecutiveDays = profile["login_count_days_consec"] resp.userStatus.loginsToday = profile["login_count_today"] resp.userStatus.rating = profile["rating"] if self.game_config.mods.infinite_wp: resp.userStatus.wp = 999999 for fav in profile_favorites: resp.favorites.append(fav["song_id"]) if profile["friend_view_1"] is not None: pass if profile["friend_view_2"] is not None: pass if profile["friend_view_3"] is not None: pass resp.seasonalPlayModeCounts.append( PlayModeCounts(self.season, 1, profile["playcount_single"]) ) resp.seasonalPlayModeCounts.append( PlayModeCounts(self.season, 2, profile["playcount_multi_vs"]) ) resp.seasonalPlayModeCounts.append( PlayModeCounts(self.season, 3, profile["playcount_multi_coop"]) ) resp.seasonalPlayModeCounts.append( PlayModeCounts(self.season, 4, profile["playcount_stageup"]) ) for opt in profile_options: resp.options.append(UserOption(opt["opt_id"], opt["value"])) for gate in self.game_config.gates.enabled_gates: added_gate = False for user_gate in profile_gates: if user_gate["gate_id"] == gate: if req.appVersion.minor >= 53: resp.gateInfo.append( GateDetailV2( user_gate["gate_id"], user_gate["page"], user_gate["progress"], user_gate["loops"], int(user_gate["last_used"].timestamp()), user_gate["mission_flag"], ) ) else: resp.gateInfo.append( GateDetailV1( user_gate["gate_id"], user_gate["page"], user_gate["progress"], user_gate["loops"], int(user_gate["last_used"].timestamp()), user_gate["mission_flag"], ) ) resp.seasonInfo.cumulativeGatePts += user_gate["total_points"] added_gate = True break if not added_gate: if req.appVersion.minor >= 53: resp.gateInfo.append(GateDetailV2(gate)) else: resp.gateInfo.append(GateDetailV1(gate)) for unlock in profile_song_unlocks: for x in range(1, unlock["highest_difficulty"] + 1): resp.userItems.songUnlocks.append( SongUnlock( unlock["song_id"], x, 0, int(unlock["acquire_date"].timestamp()) ) ) for song in profile_scores: resp.seasonInfo.cumulativeScore += song["score"] clear_cts = SongDetailClearCounts( song["play_ct"], song["clear_ct"], song["missless_ct"], song["fullcombo_ct"], song["allmarv_ct"], ) grade_cts = SongDetailGradeCountsV1( song["grade_d_ct"], song["grade_c_ct"], song["grade_b_ct"], song["grade_a_ct"], song["grade_aa_ct"], song["grade_aaa_ct"], song["grade_s_ct"], song["grade_ss_ct"], song["grade_sss_ct"], song["grade_master_ct"], ) deets = BestScoreDetailV1(song["song_id"], song["chart_id"]) deets.clearCounts = clear_cts deets.clearCountsSeason = clear_cts deets.gradeCounts = grade_cts deets.score = song["score"] deets.bestCombo = song["best_combo"] deets.lowestMissCtMaybe = song["lowest_miss_ct"] deets.rating = song["rating"] resp.scores.append(deets) for trophy in profile_trophies: resp.userItems.trophies.append( TrophyItem( trophy["trophy_id"], trophy["season"], trophy["progress"], trophy["badge_type"], ) ) if self.game_config.mods.infinite_tickets: for x in range(5): resp.userItems.tickets.append(TicketItem(x, 106002, 0)) else: for ticket in profile_tickets: if ticket["expire_date"] is None: expire = int((self.srvtime + timedelta(days=30)).timestamp()) else: expire = int(ticket["expire_date"].timestamp()) resp.userItems.tickets.append( TicketItem(ticket["id"], ticket["ticket_id"], expire) ) if profile_items: for item in profile_items: try: if item["type"] == WaccaConstants.ITEM_TYPES["icon"]: resp.userItems.icons.append( IconItem( item["item_id"], 1, item["use_count"], int(item["acquire_date"].timestamp()), ) ) elif item["type"] == WaccaConstants.ITEM_TYPES["navigator"]: resp.userItems.navigators.append( NavigatorItem( item["item_id"], 1, int(item["acquire_date"].timestamp()), item["use_count"], item["use_count"], ) ) else: itm_send = GenericItemSend( item["item_id"], 1, int(item["acquire_date"].timestamp()) ) if item["type"] == WaccaConstants.ITEM_TYPES["title"]: resp.userItems.titles.append(itm_send) elif item["type"] == WaccaConstants.ITEM_TYPES["user_plate"]: resp.userItems.plates.append(itm_send) elif item["type"] == WaccaConstants.ITEM_TYPES["note_color"]: resp.userItems.noteColors.append(itm_send) elif item["type"] == WaccaConstants.ITEM_TYPES["note_sound"]: resp.userItems.noteSounds.append(itm_send) except Exception: self.logger.error( f"Failed to load item {item['item_id']} for user {profile['user']}" ) resp.seasonInfo.level = profile["xp"] resp.seasonInfo.wpObtained = profile["wp_total"] resp.seasonInfo.wpSpent = profile["wp_spent"] resp.seasonInfo.titlesObtained = len(resp.userItems.titles) resp.seasonInfo.iconsObtained = len(resp.userItems.icons) resp.seasonInfo.noteColorsObtained = len(resp.userItems.noteColors) resp.seasonInfo.noteSoundsObtained = len(resp.userItems.noteSounds) resp.seasonInfo.platesObtained = len(resp.userItems.plates) return resp.make() async def handle_user_info_getMyroom_request(self, data: Dict) -> Dict: return UserInfogetMyroomResponseV2().make() async def handle_user_status_update_request(self, data: Dict) -> Dict: await super().handle_user_status_update_request(data) req = UserStatusUpdateRequestV2(data) await self.data.profile.update_profile_lastplayed( req.profileId, req.lastSongInfo.lastSongId, req.lastSongInfo.lastSongDiff, req.lastSongInfo.lastFolderOrd, req.lastSongInfo.lastFolderId, req.lastSongInfo.lastSongOrd, ) return BaseResponse().make()