2023-11-09 02:17:48 +00:00
from datetime import datetime , timedelta
2023-07-05 10:47:43 -04:00
from typing import Any , Dict , List
2023-02-17 01:02:21 -05:00
import logging
2023-07-16 16:06:34 -04:00
from base64 import b64decode
2023-07-16 16:58:18 -04:00
from os import path , stat , remove
from PIL import ImageFile
2023-02-17 01:02:21 -05:00
2023-10-16 13:21:07 +00:00
import pytz
2023-02-17 01:02:21 -05:00
from core.config import CoreConfig
2023-11-09 02:17:48 +00:00
from core.utils import Utils
from .const import Mai2Constants
from .config import Mai2Config
from .database import Mai2Data
2023-02-17 01:02:21 -05:00
2023-03-09 11:38:58 -05:00
class Mai2Base :
2023-02-17 01:02:21 -05:00
def __init__ ( self , cfg : CoreConfig , game_cfg : Mai2Config ) -> None :
self . core_config = cfg
self . game_config = game_cfg
2023-04-30 22:19:31 -04:00
self . version = Mai2Constants . VER_MAIMAI
2023-02-17 01:02:21 -05:00
self . data = Mai2Data ( cfg )
self . logger = logging . getLogger ( "mai2" )
2023-05-10 02:31:30 -04:00
self . can_deliver = False
self . can_usbdl = False
self . old_server = ""
2023-11-09 02:17:48 +00:00
self . date_time_format = "%Y-%m- %d %H:%M:%S"
2023-10-16 13:21:07 +00:00
2023-11-09 02:17:48 +00:00
if not self . core_config . server . is_using_proxy and Utils . get_title_port ( self . core_config ) != 80 :
self . old_server = f "http:// { self . core_config . title . hostname } : { Utils . get_title_port ( cfg ) } /197/MaimaiServlet/"
2023-10-16 13:21:07 +00:00
2023-05-03 22:29:08 -04:00
else :
2023-11-09 02:17:48 +00:00
self . old_server = f "http:// { self . core_config . title . hostname } /197/MaimaiServlet/"
2023-02-17 01:02:21 -05:00
def handle_get_game_setting_api_request ( self , data : Dict ):
2023-10-16 13:21:07 +00:00
# if reboot start/end time is not defined use the default behavior of being a few hours ago
2023-11-09 02:17:48 +00:00
if self . core_config . title . reboot_start_time == "" or self . core_config . title . reboot_end_time == "" :
2023-10-16 13:21:07 +00:00
reboot_start = datetime . strftime (
datetime . utcnow () + timedelta ( hours = 6 ), self . date_time_format
)
reboot_end = datetime . strftime (
datetime . utcnow () + timedelta ( hours = 7 ), self . date_time_format
)
else :
# get current datetime in JST
current_jst = datetime . now ( pytz . timezone ( 'Asia/Tokyo' )) . date ()
# parse config start/end times into datetime
2023-11-09 02:17:48 +00:00
reboot_start_time = datetime . strptime ( self . core_config . title . reboot_start_time , "%H:%M" )
reboot_end_time = datetime . strptime ( self . core_config . title . reboot_end_time , "%H:%M" )
2023-10-16 13:21:07 +00:00
# offset datetimes with current date/time
reboot_start_time = reboot_start_time . replace ( year = current_jst . year , month = current_jst . month , day = current_jst . day , tzinfo = pytz . timezone ( 'Asia/Tokyo' ))
reboot_end_time = reboot_end_time . replace ( year = current_jst . year , month = current_jst . month , day = current_jst . day , tzinfo = pytz . timezone ( 'Asia/Tokyo' ))
# create strings for use in gameSetting
reboot_start = reboot_start_time . strftime ( self . date_time_format )
reboot_end = reboot_end_time . strftime ( self . date_time_format )
return {
2023-05-11 09:52:18 -04:00
"isDevelop" : False ,
"isAouAccession" : False ,
2023-03-09 11:38:58 -05:00
"gameSetting" : {
2023-05-11 09:52:18 -04:00
"isMaintenance" : False ,
"requestInterval" : 1800 ,
2023-10-16 13:21:07 +00:00
"rebootStartTime" : reboot_start ,
"rebootEndTime" : reboot_end ,
2023-05-11 09:52:18 -04:00
"movieUploadLimit" : 100 ,
"movieStatus" : 1 ,
2023-06-25 19:10:34 -04:00
"movieServerUri" : self . old_server + "api/movie" if self . game_config . uploads . movies else "movie" ,
2023-05-10 02:31:30 -04:00
"deliverServerUri" : self . old_server + "deliver/" if self . can_deliver and self . game_config . deliver . enable else "" ,
2023-05-06 19:04:10 -04:00
"oldServerUri" : self . old_server + "old" ,
2023-05-10 02:31:30 -04:00
"usbDlServerUri" : self . old_server + "usbdl/" if self . can_deliver and self . game_config . deliver . udbdl_enable else "" ,
2023-02-17 01:02:21 -05:00
},
}
def handle_get_game_ranking_api_request ( self , data : Dict ) -> Dict :
return { "length" : 0 , "gameRankingList" : []}
def handle_get_game_tournament_info_api_request ( self , data : Dict ) -> Dict :
# TODO: Tournament support
return { "length" : 0 , "gameTournamentInfoList" : []}
def handle_get_game_event_api_request ( self , data : Dict ) -> Dict :
events = self . data . static . get_enabled_events ( self . version )
events_lst = []
2023-05-06 19:04:10 -04:00
if events is None or not events :
2023-08-08 10:17:56 -04:00
self . logger . warning ( "No enabled events, did you run the reader?" )
2023-03-09 11:38:58 -05:00
return { "type" : data [ "type" ], "length" : 0 , "gameEventList" : []}
2023-02-17 01:02:21 -05:00
for event in events :
2023-03-09 11:38:58 -05:00
events_lst . append (
{
"type" : event [ "type" ],
"id" : event [ "eventId" ],
2023-04-10 18:58:19 +02:00
# actually use the startDate from the import so it
# properly shows all the events when new ones are imported
"startDate" : datetime . strftime (
event [ "startDate" ], f " { Mai2Constants . DATE_TIME_FORMAT } .0"
),
2023-03-09 11:38:58 -05:00
"endDate" : "2099-12-31 00:00:00.0" ,
}
)
2023-02-17 01:02:21 -05:00
2023-03-09 11:38:58 -05:00
return {
"type" : data [ "type" ],
"length" : len ( events_lst ),
"gameEventList" : events_lst ,
}
2023-02-17 01:02:21 -05:00
def handle_get_game_ng_music_id_api_request ( self , data : Dict ) -> Dict :
return { "length" : 0 , "musicIdList" : []}
def handle_get_game_charge_api_request ( self , data : Dict ) -> Dict :
game_charge_list = self . data . static . get_enabled_tickets ( self . version , 1 )
2023-03-09 11:38:58 -05:00
if game_charge_list is None :
return { "length" : 0 , "gameChargeList" : []}
2023-02-17 01:02:21 -05:00
charge_list = []
2023-04-10 18:58:19 +02:00
for i , charge in enumerate ( game_charge_list ):
2023-03-09 11:38:58 -05:00
charge_list . append (
{
2023-07-16 16:58:18 -04:00
"orderId" : i + 1 ,
2023-04-10 18:58:19 +02:00
"chargeId" : charge [ "ticketId" ],
"price" : charge [ "price" ],
2023-03-09 11:38:58 -05:00
"startDate" : "2017-12-05 07:00:00.0" ,
"endDate" : "2099-12-31 00:00:00.0" ,
}
)
2023-02-17 01:02:21 -05:00
return { "length" : len ( charge_list ), "gameChargeList" : charge_list }
def handle_upsert_client_setting_api_request ( self , data : Dict ) -> Dict :
2023-04-11 17:57:21 +02:00
return { "returnCode" : 1 , "apiName" : "UpsertClientSettingApi" }
2023-02-17 01:02:21 -05:00
def handle_upsert_client_upload_api_request ( self , data : Dict ) -> Dict :
2023-04-11 17:57:21 +02:00
return { "returnCode" : 1 , "apiName" : "UpsertClientUploadApi" }
2023-02-17 01:02:21 -05:00
def handle_upsert_client_bookkeeping_api_request ( self , data : Dict ) -> Dict :
2023-04-11 17:57:21 +02:00
return { "returnCode" : 1 , "apiName" : "UpsertClientBookkeepingApi" }
2023-02-17 01:02:21 -05:00
def handle_upsert_client_testmode_api_request ( self , data : Dict ) -> Dict :
2023-04-11 17:57:21 +02:00
return { "returnCode" : 1 , "apiName" : "UpsertClientTestmodeApi" }
2023-02-17 01:02:21 -05:00
def handle_get_user_preview_api_request ( self , data : Dict ) -> Dict :
2023-07-01 02:27:26 -04:00
p = self . data . profile . get_profile_detail ( data [ "userId" ], self . version , False )
2023-07-01 01:56:52 -04:00
w = self . data . profile . get_web_option ( data [ "userId" ], self . version )
2023-07-01 01:08:54 -04:00
if p is None or w is None :
2023-03-09 11:38:58 -05:00
return {} # Register
2023-02-17 01:02:21 -05:00
profile = p . _asdict ()
2023-07-01 01:08:54 -04:00
web_opt = w . _asdict ()
2023-02-17 01:02:21 -05:00
return {
"userId" : data [ "userId" ],
"userName" : profile [ "userName" ],
"isLogin" : False ,
"lastDataVersion" : profile [ "lastDataVersion" ],
2023-07-01 02:27:26 -04:00
"lastLoginDate" : profile [ "lastPlayDate" ],
2023-02-17 01:02:21 -05:00
"lastPlayDate" : profile [ "lastPlayDate" ],
"playerRating" : profile [ "playerRating" ],
2023-07-01 01:08:54 -04:00
"nameplateId" : profile [ "nameplateId" ],
2023-05-06 19:04:10 -04:00
"frameId" : profile [ "frameId" ],
2023-02-17 01:02:21 -05:00
"iconId" : profile [ "iconId" ],
2023-07-01 01:08:54 -04:00
"trophyId" : profile [ "trophyId" ],
"dispRate" : web_opt [ "dispRate" ], # 0: all, 1: dispRate, 2: dispDan, 3: hide
"dispRank" : web_opt [ "dispRank" ],
"dispHomeRanker" : web_opt [ "dispHomeRanker" ],
"dispTotalLv" : web_opt [ "dispTotalLv" ],
"totalLv" : profile [ "totalLv" ],
2023-02-17 01:02:21 -05:00
}
2023-03-09 11:38:58 -05:00
2023-02-17 01:02:21 -05:00
def handle_user_login_api_request ( self , data : Dict ) -> Dict :
profile = self . data . profile . get_profile_detail ( data [ "userId" ], self . version )
2023-07-01 21:51:18 -04:00
consec = self . data . profile . get_consec_login ( data [ "userId" ], self . version )
2023-02-17 01:02:21 -05:00
if profile is not None :
lastLoginDate = profile [ "lastLoginDate" ]
loginCt = profile [ "playCount" ]
if "regionId" in data :
self . data . profile . put_profile_region ( data [ "userId" ], data [ "regionId" ])
else :
loginCt = 0
lastLoginDate = "2017-12-05 07:00:00.0"
2023-07-01 21:51:18 -04:00
if consec is None or not consec :
consec_ct = 1
else :
lastlogindate_ = datetime . strptime ( profile [ "lastLoginDate" ], "%Y-%m- %d %H:%M:%S. %f " ) . timestamp ()
today_midnight = datetime . now () . replace ( hour = 0 , minute = 0 , second = 0 , microsecond = 0 ) . timestamp ()
yesterday_midnight = today_midnight - 86400
if lastlogindate_ < today_midnight :
consec_ct = consec [ 'logins' ] + 1
self . data . profile . add_consec_login ( data [ "userId" ], self . version )
elif lastlogindate_ < yesterday_midnight :
consec_ct = 1
self . data . profile . reset_consec_login ( data [ "userId" ], self . version )
else :
consec_ct = consec [ 'logins' ]
2023-02-17 01:02:21 -05:00
return {
"returnCode" : 1 ,
"lastLoginDate" : lastLoginDate ,
"loginCount" : loginCt ,
2023-07-01 21:51:18 -04:00
"consecutiveLoginCount" : consec_ct , # Number of consecutive days we've logged in.
2023-02-17 01:02:21 -05:00
}
2023-03-09 11:38:58 -05:00
2023-02-17 01:02:21 -05:00
def handle_upload_user_playlog_api_request ( self , data : Dict ) -> Dict :
user_id = data [ "userId" ]
playlog = data [ "userPlaylog" ]
self . data . score . put_playlog ( user_id , playlog )
2023-04-11 17:57:21 +02:00
return { "returnCode" : 1 , "apiName" : "UploadUserPlaylogApi" }
2023-04-10 18:58:19 +02:00
def handle_upsert_user_chargelog_api_request ( self , data : Dict ) -> Dict :
user_id = data [ "userId" ]
charge = data [ "userCharge" ]
# remove the ".0" from the date string, festival only?
charge [ "purchaseDate" ] = charge [ "purchaseDate" ] . replace ( ".0" , "" )
self . data . item . put_charge (
user_id ,
charge [ "chargeId" ],
charge [ "stock" ],
datetime . strptime ( charge [ "purchaseDate" ], Mai2Constants . DATE_TIME_FORMAT ),
datetime . strptime ( charge [ "validDate" ], Mai2Constants . DATE_TIME_FORMAT ),
)
2023-04-11 17:57:21 +02:00
return { "returnCode" : 1 , "apiName" : "UpsertUserChargelogApi" }
2023-02-17 01:02:21 -05:00
def handle_upsert_user_all_api_request ( self , data : Dict ) -> Dict :
user_id = data [ "userId" ]
upsert = data [ "upsertUserAll" ]
if "userData" in upsert and len ( upsert [ "userData" ]) > 0 :
upsert [ "userData" ][ 0 ] . pop ( "accessCode" )
2023-07-01 01:08:54 -04:00
upsert [ "userData" ][ 0 ] . pop ( "userId" )
2023-03-09 11:38:58 -05:00
self . data . profile . put_profile_detail (
2023-07-01 01:08:54 -04:00
user_id , self . version , upsert [ "userData" ][ 0 ], False
2023-03-09 11:38:58 -05:00
)
2023-07-01 01:08:54 -04:00
2023-07-01 02:19:19 -04:00
if "userWebOption" in upsert and len ( upsert [ "userWebOption" ]) > 0 :
upsert [ "userWebOption" ][ 0 ][ "isNetMember" ] = True
2023-07-01 01:08:54 -04:00
self . data . profile . put_web_option (
2023-07-01 02:19:19 -04:00
user_id , self . version , upsert [ "userWebOption" ][ 0 ]
2023-07-01 01:08:54 -04:00
)
if "userGradeStatusList" in upsert and len ( upsert [ "userGradeStatusList" ]) > 0 :
2023-07-01 01:41:34 -04:00
self . data . profile . put_grade_status (
user_id , upsert [ "userGradeStatusList" ][ 0 ]
2023-07-01 01:08:54 -04:00
)
if "userBossList" in upsert and len ( upsert [ "userBossList" ]) > 0 :
self . data . profile . put_boss_list (
2023-07-01 01:41:34 -04:00
user_id , upsert [ "userBossList" ][ 0 ]
2023-07-01 01:08:54 -04:00
)
if "userPlaylogList" in upsert and len ( upsert [ "userPlaylogList" ]) > 0 :
for playlog in upsert [ "userPlaylogList" ]:
self . data . score . put_playlog (
2023-07-01 01:59:19 -04:00
user_id , playlog , False
2023-07-01 01:08:54 -04:00
)
2023-03-09 11:38:58 -05:00
2023-02-17 01:02:21 -05:00
if "userExtend" in upsert and len ( upsert [ "userExtend" ]) > 0 :
2023-03-09 11:38:58 -05:00
self . data . profile . put_profile_extend (
user_id , self . version , upsert [ "userExtend" ][ 0 ]
)
2023-02-17 01:02:21 -05:00
if "userGhost" in upsert :
for ghost in upsert [ "userGhost" ]:
2023-07-01 01:08:54 -04:00
self . data . profile . put_profile_ghost ( user_id , self . version , ghost )
if "userRecentRatingList" in upsert :
2023-07-01 01:42:38 -04:00
self . data . profile . put_recent_rating ( user_id , upsert [ "userRecentRatingList" ])
2023-03-09 11:38:58 -05:00
2023-02-17 01:02:21 -05:00
if "userOption" in upsert and len ( upsert [ "userOption" ]) > 0 :
2023-07-01 01:56:52 -04:00
upsert [ "userOption" ][ 0 ] . pop ( "userId" )
2023-03-09 11:38:58 -05:00
self . data . profile . put_profile_option (
2023-07-01 01:08:54 -04:00
user_id , self . version , upsert [ "userOption" ][ 0 ], False
2023-03-09 11:38:58 -05:00
)
2023-02-17 01:02:21 -05:00
if "userRatingList" in upsert and len ( upsert [ "userRatingList" ]) > 0 :
2023-03-09 11:38:58 -05:00
self . data . profile . put_profile_rating (
user_id , self . version , upsert [ "userRatingList" ][ 0 ]
)
2023-02-17 01:02:21 -05:00
if "userActivityList" in upsert and len ( upsert [ "userActivityList" ]) > 0 :
2023-07-01 01:56:52 -04:00
for act in upsert [ "userActivityList" ]:
self . data . profile . put_profile_activity ( user_id , act )
2023-02-17 01:02:21 -05:00
2023-03-15 20:03:22 +00:00
if "userChargeList" in upsert and len ( upsert [ "userChargeList" ]) > 0 :
for charge in upsert [ "userChargeList" ]:
2023-04-10 18:58:19 +02:00
# remove the ".0" from the date string, festival only?
charge [ "purchaseDate" ] = charge [ "purchaseDate" ] . replace ( ".0" , "" )
2023-03-15 20:03:22 +00:00
self . data . item . put_charge (
user_id ,
charge [ "chargeId" ],
charge [ "stock" ],
2023-04-10 18:58:19 +02:00
datetime . strptime (
charge [ "purchaseDate" ], Mai2Constants . DATE_TIME_FORMAT
),
datetime . strptime (
charge [ "validDate" ], Mai2Constants . DATE_TIME_FORMAT
),
2023-03-15 20:03:22 +00:00
)
2023-04-10 18:58:19 +02:00
if "userCharacterList" in upsert and len ( upsert [ "userCharacterList" ]) > 0 :
2023-02-17 01:02:21 -05:00
for char in upsert [ "userCharacterList" ]:
2023-07-01 02:04:30 -04:00
self . data . item . put_character_ (
2023-03-09 11:38:58 -05:00
user_id ,
2023-07-01 02:04:30 -04:00
char
2023-03-09 11:38:58 -05:00
)
2023-02-17 01:02:21 -05:00
2023-04-10 18:58:19 +02:00
if "userItemList" in upsert and len ( upsert [ "userItemList" ]) > 0 :
2023-02-17 01:02:21 -05:00
for item in upsert [ "userItemList" ]:
2023-03-09 11:38:58 -05:00
self . data . item . put_item (
user_id ,
int ( item [ "itemKind" ]),
item [ "itemId" ],
item [ "stock" ],
2023-07-01 02:04:30 -04:00
True
2023-03-09 11:38:58 -05:00
)
2023-02-17 01:02:21 -05:00
2023-04-10 18:58:19 +02:00
if "userLoginBonusList" in upsert and len ( upsert [ "userLoginBonusList" ]) > 0 :
2023-02-17 01:02:21 -05:00
for login_bonus in upsert [ "userLoginBonusList" ]:
2023-03-09 11:38:58 -05:00
self . data . item . put_login_bonus (
user_id ,
login_bonus [ "bonusId" ],
login_bonus [ "point" ],
login_bonus [ "isCurrent" ],
login_bonus [ "isComplete" ],
)
2023-02-17 01:02:21 -05:00
2023-04-10 18:58:19 +02:00
if "userMapList" in upsert and len ( upsert [ "userMapList" ]) > 0 :
2023-02-17 01:02:21 -05:00
for map in upsert [ "userMapList" ]:
2023-03-09 11:38:58 -05:00
self . data . item . put_map (
user_id ,
map [ "mapId" ],
map [ "distance" ],
map [ "isLock" ],
map [ "isClear" ],
map [ "isComplete" ],
)
2023-04-10 18:58:19 +02:00
if "userMusicDetailList" in upsert and len ( upsert [ "userMusicDetailList" ]) > 0 :
2023-02-17 01:02:21 -05:00
for music in upsert [ "userMusicDetailList" ]:
2023-07-01 01:56:52 -04:00
self . data . score . put_best_score ( user_id , music , False )
2023-03-09 11:38:58 -05:00
2023-04-10 18:58:19 +02:00
if "userCourseList" in upsert and len ( upsert [ "userCourseList" ]) > 0 :
2023-02-17 01:02:21 -05:00
for course in upsert [ "userCourseList" ]:
self . data . score . put_course ( user_id , course )
2023-03-09 11:38:58 -05:00
2023-04-10 18:58:19 +02:00
if "userFavoriteList" in upsert and len ( upsert [ "userFavoriteList" ]) > 0 :
2023-02-17 01:02:21 -05:00
for fav in upsert [ "userFavoriteList" ]:
self . data . item . put_favorite ( user_id , fav [ "kind" ], fav [ "itemIdList" ])
2023-04-10 18:58:19 +02:00
if (
"userFriendSeasonRankingList" in upsert
and len ( upsert [ "userFriendSeasonRankingList" ]) > 0
):
for fsr in upsert [ "userFriendSeasonRankingList" ]:
fsr [ "recordDate" ] = (
2023-04-23 21:04:52 -04:00
datetime . strptime (
fsr [ "recordDate" ], f " { Mai2Constants . DATE_TIME_FORMAT } .0"
),
2023-04-10 18:58:19 +02:00
)
self . data . item . put_friend_season_ranking ( user_id , fsr )
2023-02-17 01:02:21 -05:00
2023-04-11 17:57:21 +02:00
return { "returnCode" : 1 , "apiName" : "UpsertUserAllApi" }
2023-02-17 01:02:21 -05:00
def handle_user_logout_api_request ( self , data : Dict ) -> Dict :
2023-04-11 17:57:21 +02:00
return { "returnCode" : 1 }
2023-02-17 01:02:21 -05:00
def handle_get_user_data_api_request ( self , data : Dict ) -> Dict :
2023-07-01 01:08:54 -04:00
profile = self . data . profile . get_profile_detail ( data [ "userId" ], self . version , False )
2023-03-09 11:38:58 -05:00
if profile is None :
return
2023-02-17 01:02:21 -05:00
profile_dict = profile . _asdict ()
profile_dict . pop ( "id" )
profile_dict . pop ( "user" )
profile_dict . pop ( "version" )
2023-03-09 11:38:58 -05:00
return { "userId" : data [ "userId" ], "userData" : profile_dict }
2023-02-17 01:02:21 -05:00
def handle_get_user_extend_api_request ( self , data : Dict ) -> Dict :
extend = self . data . profile . get_profile_extend ( data [ "userId" ], self . version )
2023-03-09 11:38:58 -05:00
if extend is None :
return
2023-02-17 01:02:21 -05:00
extend_dict = extend . _asdict ()
extend_dict . pop ( "id" )
extend_dict . pop ( "user" )
extend_dict . pop ( "version" )
2023-03-09 11:38:58 -05:00
return { "userId" : data [ "userId" ], "userExtend" : extend_dict }
2023-02-17 01:02:21 -05:00
def handle_get_user_option_api_request ( self , data : Dict ) -> Dict :
2023-07-01 01:08:54 -04:00
options = self . data . profile . get_profile_option ( data [ "userId" ], self . version , False )
2023-03-09 11:38:58 -05:00
if options is None :
return
2023-02-17 01:02:21 -05:00
options_dict = options . _asdict ()
options_dict . pop ( "id" )
options_dict . pop ( "user" )
options_dict . pop ( "version" )
2023-03-09 11:38:58 -05:00
return { "userId" : data [ "userId" ], "userOption" : options_dict }
2023-02-17 01:02:21 -05:00
def handle_get_user_card_api_request ( self , data : Dict ) -> Dict :
2023-03-15 20:03:22 +00:00
user_cards = self . data . item . get_cards ( data [ "userId" ])
if user_cards is None :
2023-04-10 18:58:19 +02:00
return { "userId" : data [ "userId" ], "nextIndex" : 0 , "userCardList" : []}
2023-03-15 20:03:22 +00:00
max_ct = data [ "maxCount" ]
next_idx = data [ "nextIndex" ]
start_idx = next_idx
end_idx = max_ct + start_idx
if len ( user_cards [ start_idx :]) > max_ct :
next_idx += max_ct
else :
next_idx = 0
card_list = []
for card in user_cards :
tmp = card . _asdict ()
tmp . pop ( "id" )
tmp . pop ( "user" )
tmp [ "startDate" ] = datetime . strftime (
2023-04-10 18:58:19 +02:00
tmp [ "startDate" ], Mai2Constants . DATE_TIME_FORMAT
)
2023-03-15 20:03:22 +00:00
tmp [ "endDate" ] = datetime . strftime (
2023-04-10 18:58:19 +02:00
tmp [ "endDate" ], Mai2Constants . DATE_TIME_FORMAT
)
2023-03-15 20:03:22 +00:00
card_list . append ( tmp )
return {
"userId" : data [ "userId" ],
"nextIndex" : next_idx ,
2023-04-10 18:58:19 +02:00
"userCardList" : card_list [ start_idx : end_idx ],
2023-03-15 20:03:22 +00:00
}
2023-02-17 01:02:21 -05:00
def handle_get_user_charge_api_request ( self , data : Dict ) -> Dict :
2023-03-15 20:03:22 +00:00
user_charges = self . data . item . get_charges ( data [ "userId" ])
if user_charges is None :
2023-04-10 18:58:19 +02:00
return { "userId" : data [ "userId" ], "length" : 0 , "userChargeList" : []}
2023-03-15 20:03:22 +00:00
user_charge_list = []
for charge in user_charges :
tmp = charge . _asdict ()
tmp . pop ( "id" )
tmp . pop ( "user" )
tmp [ "purchaseDate" ] = datetime . strftime (
2023-04-10 18:58:19 +02:00
tmp [ "purchaseDate" ], Mai2Constants . DATE_TIME_FORMAT
)
2023-03-15 20:03:22 +00:00
tmp [ "validDate" ] = datetime . strftime (
2023-04-10 18:58:19 +02:00
tmp [ "validDate" ], Mai2Constants . DATE_TIME_FORMAT
)
2023-03-15 20:03:22 +00:00
user_charge_list . append ( tmp )
return {
"userId" : data [ "userId" ],
"length" : len ( user_charge_list ),
2023-04-10 18:58:19 +02:00
"userChargeList" : user_charge_list ,
2023-03-15 20:03:22 +00:00
}
2023-02-17 01:02:21 -05:00
2023-07-01 01:08:54 -04:00
def handle_get_user_present_api_request ( self , data : Dict ) -> Dict :
return { "userId" : data . get ( "userId" , 0 ), "length" : 0 , "userPresentList" : []}
def handle_get_transfer_friend_api_request ( self , data : Dict ) -> Dict :
return {}
def handle_get_user_present_event_api_request ( self , data : Dict ) -> Dict :
return { "userId" : data . get ( "userId" , 0 ), "length" : 0 , "userPresentEventList" : []}
def handle_get_user_boss_api_request ( self , data : Dict ) -> Dict :
b = self . data . profile . get_boss_list ( data [ "userId" ])
if b is None :
return { "userId" : data . get ( "userId" , 0 ), "userBossData" : {}}
boss_lst = b . _asdict ()
boss_lst . pop ( "id" )
boss_lst . pop ( "user" )
return { "userId" : data . get ( "userId" , 0 ), "userBossData" : boss_lst }
2023-02-17 01:02:21 -05:00
def handle_get_user_item_api_request ( self , data : Dict ) -> Dict :
kind = int ( data [ "nextIndex" ] / 10000000000 )
next_idx = int ( data [ "nextIndex" ] % 10000000000 )
2023-04-10 18:58:19 +02:00
user_item_list = self . data . item . get_items ( data [ "userId" ], kind )
2023-02-17 01:02:21 -05:00
2023-07-05 10:47:43 -04:00
items : List [ Dict [ str , Any ]] = []
2023-04-10 18:58:19 +02:00
for i in range ( next_idx , len ( user_item_list )):
tmp = user_item_list [ i ] . _asdict ()
tmp . pop ( "user" )
tmp . pop ( "id" )
items . append ( tmp )
if len ( items ) >= int ( data [ "maxCount" ]):
2023-03-09 11:38:58 -05:00
break
2023-04-10 18:58:19 +02:00
xout = kind * 10000000000 + next_idx + len ( items )
if len ( items ) < int ( data [ "maxCount" ]):
next_idx = 0
else :
next_idx = xout
2023-02-17 01:02:21 -05:00
2023-03-09 11:38:58 -05:00
return {
"userId" : data [ "userId" ],
"nextIndex" : next_idx ,
"itemKind" : kind ,
2023-04-10 18:58:19 +02:00
"userItemList" : items ,
2023-03-09 11:38:58 -05:00
}
2023-02-17 01:02:21 -05:00
def handle_get_user_character_api_request ( self , data : Dict ) -> Dict :
characters = self . data . item . get_characters ( data [ "userId" ])
2023-03-15 20:03:22 +00:00
2023-02-17 01:02:21 -05:00
chara_list = []
for chara in characters :
2023-03-15 20:03:22 +00:00
tmp = chara . _asdict ()
tmp . pop ( "id" )
tmp . pop ( "user" )
2023-07-01 01:08:54 -04:00
tmp . pop ( "awakening" )
tmp . pop ( "useCount" )
2023-03-15 20:03:22 +00:00
chara_list . append ( tmp )
2023-02-17 01:02:21 -05:00
return { "userId" : data [ "userId" ], "userCharacterList" : chara_list }
2023-03-09 11:38:58 -05:00
2023-02-17 01:02:21 -05:00
def handle_get_user_favorite_api_request ( self , data : Dict ) -> Dict :
favorites = self . data . item . get_favorites ( data [ "userId" ], data [ "itemKind" ])
2023-03-09 11:38:58 -05:00
if favorites is None :
return
2023-02-17 01:02:21 -05:00
userFavs = []
for fav in favorites :
2023-03-09 11:38:58 -05:00
userFavs . append (
{
"userId" : data [ "userId" ],
"itemKind" : fav [ "itemKind" ],
"itemIdList" : fav [ "itemIdList" ],
}
)
2023-02-17 01:02:21 -05:00
2023-03-09 11:38:58 -05:00
return { "userId" : data [ "userId" ], "userFavoriteData" : userFavs }
2023-02-17 01:02:21 -05:00
def handle_get_user_ghost_api_request ( self , data : Dict ) -> Dict :
ghost = self . data . profile . get_profile_ghost ( data [ "userId" ], self . version )
2023-03-09 11:38:58 -05:00
if ghost is None :
return
2023-02-17 01:02:21 -05:00
ghost_dict = ghost . _asdict ()
ghost_dict . pop ( "user" )
ghost_dict . pop ( "id" )
ghost_dict . pop ( "version_int" )
2023-03-09 11:38:58 -05:00
return { "userId" : data [ "userId" ], "userGhost" : ghost_dict }
2023-02-17 01:02:21 -05:00
2023-07-01 01:08:54 -04:00
def handle_get_user_recent_rating_api_request ( self , data : Dict ) -> Dict :
rating = self . data . profile . get_recent_rating ( data [ "userId" ])
if rating is None :
return
r = rating . _asdict ()
lst = r . get ( "userRecentRatingList" , [])
return { "userId" : data [ "userId" ], "length" : len ( lst ), "userRecentRatingList" : lst }
2023-02-17 01:02:21 -05:00
def handle_get_user_rating_api_request ( self , data : Dict ) -> Dict :
rating = self . data . profile . get_profile_rating ( data [ "userId" ], self . version )
2023-03-09 11:38:58 -05:00
if rating is None :
return
2023-02-17 01:02:21 -05:00
rating_dict = rating . _asdict ()
rating_dict . pop ( "user" )
rating_dict . pop ( "id" )
rating_dict . pop ( "version" )
2023-03-09 11:38:58 -05:00
return { "userId" : data [ "userId" ], "userRating" : rating_dict }
2023-02-17 01:02:21 -05:00
def handle_get_user_activity_api_request ( self , data : Dict ) -> Dict :
"""
kind 1 is playlist, kind 2 is music list
"""
playlist = self . data . profile . get_profile_activity ( data [ "userId" ], 1 )
musiclist = self . data . profile . get_profile_activity ( data [ "userId" ], 2 )
2023-03-09 11:38:58 -05:00
if playlist is None or musiclist is None :
return
2023-02-17 01:02:21 -05:00
plst = []
mlst = []
for play in playlist :
2023-03-09 11:38:58 -05:00
tmp = play . _asdict ()
2023-02-17 01:02:21 -05:00
tmp [ "id" ] = tmp [ "activityId" ]
tmp . pop ( "activityId" )
tmp . pop ( "user" )
plst . append ( tmp )
for music in musiclist :
2023-03-09 11:38:58 -05:00
tmp = music . _asdict ()
2023-02-17 01:02:21 -05:00
tmp [ "id" ] = tmp [ "activityId" ]
tmp . pop ( "activityId" )
tmp . pop ( "user" )
mlst . append ( tmp )
2023-04-10 18:58:19 +02:00
return { "userActivity" : { "playList" : plst , "musicList" : mlst }}
2023-02-17 01:02:21 -05:00
def handle_get_user_course_api_request ( self , data : Dict ) -> Dict :
user_courses = self . data . score . get_courses ( data [ "userId" ])
2023-03-15 20:03:22 +00:00
if user_courses is None :
2023-04-10 18:58:19 +02:00
return { "userId" : data [ "userId" ], "nextIndex" : 0 , "userCourseList" : []}
2023-02-17 01:02:21 -05:00
course_list = []
for course in user_courses :
tmp = course . _asdict ()
tmp . pop ( "user" )
tmp . pop ( "id" )
course_list . append ( tmp )
2023-04-10 18:58:19 +02:00
return { "userId" : data [ "userId" ], "nextIndex" : 0 , "userCourseList" : course_list }
2023-02-17 01:02:21 -05:00
def handle_get_user_portrait_api_request ( self , data : Dict ) -> Dict :
# No support for custom pfps
return { "length" : 0 , "userPortraitList" : []}
def handle_get_user_friend_season_ranking_api_request ( self , data : Dict ) -> Dict :
friend_season_ranking = self . data . item . get_friend_season_ranking ( data [ "userId" ])
2023-04-10 18:58:19 +02:00
if friend_season_ranking is None :
return {
"userId" : data [ "userId" ],
"nextIndex" : 0 ,
"userFriendSeasonRankingList" : [],
}
2023-02-17 01:02:21 -05:00
2023-04-10 18:58:19 +02:00
friend_season_ranking_list = []
next_idx = int ( data [ "nextIndex" ])
max_ct = int ( data [ "maxCount" ])
for x in range ( next_idx , len ( friend_season_ranking )):
tmp = friend_season_ranking [ x ] . _asdict ()
tmp . pop ( "user" )
tmp . pop ( "id" )
tmp [ "recordDate" ] = datetime . strftime (
tmp [ "recordDate" ], f " { Mai2Constants . DATE_TIME_FORMAT } .0"
)
friend_season_ranking_list . append ( tmp )
if len ( friend_season_ranking_list ) >= max_ct :
2023-02-17 01:02:21 -05:00
break
2023-04-10 18:58:19 +02:00
if len ( friend_season_ranking ) >= next_idx + max_ct :
next_idx += max_ct
else :
next_idx = 0
2023-02-17 01:02:21 -05:00
2023-03-09 11:38:58 -05:00
return {
"userId" : data [ "userId" ],
2023-04-10 18:58:19 +02:00
"nextIndex" : next_idx ,
2023-03-09 11:38:58 -05:00
"userFriendSeasonRankingList" : friend_season_ranking_list ,
}
2023-02-17 01:02:21 -05:00
def handle_get_user_map_api_request ( self , data : Dict ) -> Dict :
maps = self . data . item . get_maps ( data [ "userId" ])
2023-04-10 18:58:19 +02:00
if maps is None :
return {
"userId" : data [ "userId" ],
"nextIndex" : 0 ,
"userMapList" : [],
}
2023-02-17 01:02:21 -05:00
2023-04-10 18:58:19 +02:00
map_list = []
next_idx = int ( data [ "nextIndex" ])
max_ct = int ( data [ "maxCount" ])
for x in range ( next_idx , len ( maps )):
tmp = maps [ x ] . _asdict ()
tmp . pop ( "user" )
tmp . pop ( "id" )
map_list . append ( tmp )
if len ( map_list ) >= max_ct :
2023-02-17 01:02:21 -05:00
break
2023-04-10 18:58:19 +02:00
if len ( maps ) >= next_idx + max_ct :
next_idx += max_ct
else :
next_idx = 0
2023-02-17 01:02:21 -05:00
2023-03-09 11:38:58 -05:00
return {
"userId" : data [ "userId" ],
2023-04-10 18:58:19 +02:00
"nextIndex" : next_idx ,
2023-03-09 11:38:58 -05:00
"userMapList" : map_list ,
}
2023-02-17 01:02:21 -05:00
def handle_get_user_login_bonus_api_request ( self , data : Dict ) -> Dict :
login_bonuses = self . data . item . get_login_bonuses ( data [ "userId" ])
2023-04-10 18:58:19 +02:00
if login_bonuses is None :
return {
"userId" : data [ "userId" ],
"nextIndex" : 0 ,
"userLoginBonusList" : [],
}
2023-02-17 01:02:21 -05:00
2023-04-10 18:58:19 +02:00
login_bonus_list = []
next_idx = int ( data [ "nextIndex" ])
max_ct = int ( data [ "maxCount" ])
for x in range ( next_idx , len ( login_bonuses )):
tmp = login_bonuses [ x ] . _asdict ()
tmp . pop ( "user" )
tmp . pop ( "id" )
login_bonus_list . append ( tmp )
if len ( login_bonus_list ) >= max_ct :
2023-02-17 01:02:21 -05:00
break
2023-04-10 18:58:19 +02:00
if len ( login_bonuses ) >= next_idx + max_ct :
next_idx += max_ct
else :
next_idx = 0
2023-02-17 01:02:21 -05:00
2023-03-09 11:38:58 -05:00
return {
"userId" : data [ "userId" ],
2023-04-10 18:58:19 +02:00
"nextIndex" : next_idx ,
2023-03-09 11:38:58 -05:00
"userLoginBonusList" : login_bonus_list ,
}
2023-02-17 01:02:21 -05:00
def handle_get_user_region_api_request ( self , data : Dict ) -> Dict :
return { "userId" : data [ "userId" ], "length" : 0 , "userRegionList" : []}
2023-07-01 02:40:07 -04:00
def handle_get_user_web_option_api_request ( self , data : Dict ) -> Dict :
w = self . data . profile . get_web_option ( data [ "userId" ], self . version )
if w is None :
return { "userId" : data [ "userId" ], "userWebOption" : {}}
web_opt = w . _asdict ()
web_opt . pop ( "id" )
web_opt . pop ( "user" )
web_opt . pop ( "version" )
return { "userId" : data [ "userId" ], "userWebOption" : web_opt }
def handle_get_user_survival_api_request ( self , data : Dict ) -> Dict :
return { "userId" : data [ "userId" ], "length" : 0 , "userSurvivalList" : []}
def handle_get_user_grade_api_request ( self , data : Dict ) -> Dict :
g = self . data . profile . get_grade_status ( data [ "userId" ])
if g is None :
return { "userId" : data [ "userId" ], "userGradeStatus" : {}, "length" : 0 , "userGradeList" : []}
grade_stat = g . _asdict ()
grade_stat . pop ( "id" )
grade_stat . pop ( "user" )
return { "userId" : data [ "userId" ], "userGradeStatus" : grade_stat , "length" : 0 , "userGradeList" : []}
2023-02-17 01:02:21 -05:00
def handle_get_user_music_api_request ( self , data : Dict ) -> Dict :
2023-06-13 22:35:09 -04:00
user_id = data . get ( "userId" , 0 )
next_index = data . get ( "nextIndex" , 0 )
max_ct = data . get ( "maxCount" , 50 )
upper_lim = next_index + max_ct
2023-02-17 01:02:21 -05:00
music_detail_list = []
2023-06-13 22:35:09 -04:00
if user_id <= 0 :
2023-08-08 10:17:56 -04:00
self . logger . warning ( "handle_get_user_music_api_request: Could not find userid in data, or userId is 0" )
2023-06-13 22:35:09 -04:00
return {}
2023-07-01 02:40:07 -04:00
songs = self . data . score . get_best_scores ( user_id , is_dx = False )
2023-06-13 22:07:48 -04:00
if songs is None :
2023-06-13 22:35:09 -04:00
self . logger . debug ( "handle_get_user_music_api_request: get_best_scores returned None!" )
2023-06-13 22:07:48 -04:00
return {
"userId" : data [ "userId" ],
"nextIndex" : 0 ,
"userMusicList" : [],
}
2023-03-15 20:03:22 +00:00
2023-06-13 22:07:48 -04:00
num_user_songs = len ( songs )
2023-02-17 01:02:21 -05:00
2023-06-13 22:07:48 -04:00
for x in range ( next_index , upper_lim ):
2023-06-13 22:50:57 -04:00
if num_user_songs <= x :
2023-06-13 22:07:48 -04:00
break
2023-03-15 20:03:22 +00:00
2023-06-13 22:07:48 -04:00
tmp = songs [ x ] . _asdict ()
tmp . pop ( "id" )
tmp . pop ( "user" )
music_detail_list . append ( tmp )
2023-02-17 01:02:21 -05:00
2023-06-13 22:10:35 -04:00
next_index = 0 if len ( music_detail_list ) < max_ct or num_user_songs == upper_lim else upper_lim
2023-06-13 22:35:09 -04:00
self . logger . info ( f "Send songs { next_index } - { upper_lim } ( { len ( music_detail_list ) } ) out of { num_user_songs } for user { user_id } (next idx { next_index } )" )
2023-03-09 11:38:58 -05:00
return {
"userId" : data [ "userId" ],
"nextIndex" : next_index ,
2023-04-10 18:58:19 +02:00
"userMusicList" : [{ "userMusicDetailList" : music_detail_list }],
2023-03-09 11:38:58 -05:00
}
2023-07-01 21:51:18 -04:00
def handle_upload_user_portrait_api_request ( self , data : Dict ) -> Dict :
self . logger . debug ( data )
def handle_upload_user_photo_api_request ( self , data : Dict ) -> Dict :
2023-07-16 16:06:34 -04:00
if not self . game_config . uploads . photos or not self . game_config . uploads . photos_dir :
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
photo = data . get ( "userPhoto" , {})
if photo is None or not photo :
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
order_id = int ( photo . get ( "orderId" , - 1 ))
user_id = int ( photo . get ( "userId" , - 1 ))
div_num = int ( photo . get ( "divNumber" , - 1 ))
div_len = int ( photo . get ( "divLength" , - 1 ))
div_data = photo . get ( "divData" , "" )
playlog_id = int ( photo . get ( "playlogId" , - 1 ))
track_num = int ( photo . get ( "trackNo" , - 1 ))
upload_date = photo . get ( "uploadDate" , "" )
if order_id < 0 or user_id <= 0 or div_num < 0 or div_len <= 0 or not div_data or playlog_id < 0 or track_num <= 0 or not upload_date :
2023-08-08 10:17:56 -04:00
self . logger . warning ( f "Malformed photo upload request" )
2023-07-16 16:06:34 -04:00
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
if order_id == 0 and div_num > 0 :
2023-08-08 10:17:56 -04:00
self . logger . warning ( f "Failed to set orderId properly (still 0 after first chunk)" )
2023-07-16 16:06:34 -04:00
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
if div_num == 0 and order_id > 0 :
2023-08-08 10:17:56 -04:00
self . logger . warning ( f "First chuck re-send, Ignore" )
2023-07-16 16:06:34 -04:00
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
if div_num >= div_len :
2023-08-08 10:17:56 -04:00
self . logger . warning ( f "Sent extra chunks ( { div_num } >= { div_len } )" )
2023-07-16 16:06:34 -04:00
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
if div_len >= 100 :
2023-08-08 10:17:56 -04:00
self . logger . warning ( f "Photo too large ( { div_len } * 10240 = { div_len * 10240 } bytes)" )
2023-07-16 16:06:34 -04:00
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
2023-07-16 16:58:18 -04:00
ret_code = order_id + 1
2023-07-16 16:06:34 -04:00
photo_chunk = b64decode ( div_data )
if len ( photo_chunk ) > 10240 or ( len ( photo_chunk ) < 10240 and div_num + 1 != div_len ):
2023-08-08 10:17:56 -04:00
self . logger . warning ( f "Incorrect data size after decoding (Expected 10240, got { len ( photo_chunk ) } )" )
2023-07-16 16:06:34 -04:00
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
2023-07-16 16:58:18 -04:00
out_name = f " { self . game_config . uploads . photos_dir } / { user_id } _ { playlog_id } _ { track_num } "
2023-07-16 16:06:34 -04:00
2023-07-16 16:58:18 -04:00
if not path . exists ( f " { out_name } .bin" ) and div_num != 0 :
2023-08-08 10:17:56 -04:00
self . logger . warning ( f "Out of order photo upload (div_num { div_num } )" )
2023-07-16 16:06:34 -04:00
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
2023-07-16 16:58:18 -04:00
if path . exists ( f " { out_name } .bin" ) and div_num == 0 :
2023-08-08 10:17:56 -04:00
self . logger . warning ( f "Duplicate file upload" )
2023-07-16 16:06:34 -04:00
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
2023-07-16 16:58:18 -04:00
elif path . exists ( f " { out_name } .bin" ):
fstats = stat ( f " { out_name } .bin" )
2023-07-16 16:06:34 -04:00
if fstats . st_size != 10240 * div_num :
2023-08-08 10:17:56 -04:00
self . logger . warning ( f "Out of order photo upload (trying to upload div { div_num } , expected div { fstats . st_size / 10240 } for file sized { fstats . st_size } bytes)" )
2023-07-16 16:06:34 -04:00
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
2023-07-16 16:58:18 -04:00
try :
with open ( f " { out_name } .bin" , "ab" ) as f :
f . write ( photo_chunk )
except Exception :
self . logger . error ( f "Failed writing to { out_name } .bin" )
return { 'returnCode' : 0 , 'apiName' : 'UploadUserPhotoApi' }
2023-07-16 16:06:34 -04:00
2023-07-16 16:58:18 -04:00
if div_num + 1 == div_len and path . exists ( f " { out_name } .bin" ):
try :
p = ImageFile . Parser ()
with open ( f " { out_name } .bin" , "rb" ) as f :
p . feed ( f . read ())
im = p . close ()
im . save ( f " { out_name } .jpeg" )
except Exception :
self . logger . error ( f "File { out_name } .bin failed image validation" )
try :
remove ( f " { out_name } .bin" )
except Exception :
self . logger . error ( f "Failed to delete { out_name } .bin, please remove it manually" )
return { 'returnCode' : ret_code , 'apiName' : 'UploadUserPhotoApi' }