def asyncPost(self): statusCode = 400 data = {"message": "unknown error"} try: # Check arguments if not requestsManager.checkArguments(self.request.arguments, ["sid", "refresh"]): raise exceptions.invalidArgumentsException(MODULE_NAME) # Get beatmap set data from osu api beatmapSetID = self.get_argument("sid") refresh = int(self.get_argument("refresh")) if refresh == 1: log.debug("Forced refresh") apiResponse = osuapiHelper.osuApiRequest( "get_beatmaps", "s={}".format(beatmapSetID), False) if len(apiResponse) == 0: raise exceptions.invalidBeatmapException # Loop through all beatmaps in this set and save them in db data["maps"] = [] for i in apiResponse: log.debug("Saving beatmap {} in db".format(i["file_md5"])) bmap = beatmap.beatmap(i["file_md5"], int(i["beatmapset_id"]), refresh=refresh) pp = glob.db.fetch( "SELECT pp_100 FROM beatmaps WHERE beatmap_id = %s LIMIT 1", [bmap.beatmapID]) if pp is None: pp = 0 else: pp = pp["pp_100"] data["maps"].append({ "id": bmap.beatmapID, "name": bmap.songName, "status": bmap.rankedStatus, "frozen": bmap.rankedStatusFrozen, "pp": pp, }) # Set status code and message statusCode = 200 data["message"] = "ok" except exceptions.invalidArgumentsException: # Set error and message statusCode = 400 data["message"] = "missing required arguments" except exceptions.invalidBeatmapException: statusCode = 400 data["message"] = "beatmap not found from osu!api." finally: # Add status code to data data["status"] = statusCode # Send response self.write(json.dumps(data)) self.set_header("Content-Type", "application/json") #self.add_header("Access-Control-Allow-Origin", "*") self.set_status(statusCode)
def updateSet(beatmapSetID): apiResponse = osuapiHelper.osuApiRequest("get_beatmaps", "s={}".format(beatmapSetID), False) if len(apiResponse) == 0: return for i in apiResponse: beatmap.beatmap(i["file_md5"], int(i["beatmapset_id"]), refresh=True)
def handle(self, data): data = super().parseData(data) if data is None: return if "id" in data: beatmapData = osuapiHelper.osuApiRequest("get_beatmaps", "b={}".format(data["id"])) if beatmapData is not None and "beatmapset_id" in beatmapData: updateSet(beatmapData["beatmapset_id"]) elif "set_id" in data: updateSet(data["set_id"])
def asyncGet(self): statusCode = 400 data = {"message": "unknown error"} try: # Check arguments if not requestsManager.checkArguments(self.request.arguments, ["b"]): raise exceptions.invalidArgumentsException(self.MODULE_NAME) # Get beatmap ID and make sure it's a valid number beatmapId = self.get_argument("b") if not beatmapId.isdigit(): raise exceptions.invalidArgumentsException(self.MODULE_NAME) # Get mods if "m" in self.request.arguments: modsEnum = self.get_argument("m") if not modsEnum.isdigit(): raise exceptions.invalidArgumentsException( self.MODULE_NAME) modsEnum = int(modsEnum) else: modsEnum = 0 # Get game mode if "g" in self.request.arguments: gameMode = self.get_argument("g") if not gameMode.isdigit(): raise exceptions.invalidArgumentsException( self.MODULE_NAME) gameMode = int(gameMode) else: gameMode = 0 # Get acc if "a" in self.request.arguments: accuracy = self.get_argument("a") try: accuracy = float(accuracy) except ValueError: raise exceptions.invalidArgumentsException( self.MODULE_NAME) else: accuracy = None # Print message log.info("Requested pp for beatmap {}".format(beatmapId)) # Get beatmap md5 from osuapi # TODO: Move this to beatmap object osuapiData = osuapiHelper.osuApiRequest("get_beatmaps", "b={}".format(beatmapId)) if osuapiData is None or "file_md5" not in osuapiData or "beatmapset_id" not in osuapiData: raise exceptions.invalidBeatmapException(self.MODULE_NAME) beatmapMd5 = osuapiData["file_md5"] beatmapSetID = osuapiData["beatmapset_id"] # Create beatmap object bmap = beatmap.beatmap(beatmapMd5, beatmapSetID) # Check beatmap length # TODO: Why do we do this? if bmap.hit_length > 900: raise exceptions.beatmapTooLongException(self.MODULE_NAME) if gameMode == gameModes.STD and bmap.starsStd == 0: # Mode Specific beatmap, auto detect game mode if bmap.starsTaiko > 0: gameMode = gameModes.TAIKO if bmap.starsCtb > 0: gameMode = gameModes.CTB if bmap.starsMania > 0: gameMode = gameModes.MANIA # Calculate pp if gameMode in (gameModes.STD, gameModes.TAIKO): # osu!standard and osu!taiko oppai = ez.Ez(bmap, mods_=modsEnum, tillerino=accuracy is None, acc=accuracy, gameMode=gameMode) bmap.starsStd = oppai.stars returnPP = oppai.pp elif gameMode == gameModes.CTB: # osu!catch ciccio = cicciobello.Cicciobello(bmap, mods_=modsEnum, tillerino=accuracy is None, accuracy=accuracy) bmap.starsStd = ciccio.stars returnPP = ciccio.pp else: raise exceptions.unsupportedGameModeException() # Data to return data = { "song_name": bmap.songName, "pp": [x for x in returnPP] if type(returnPP) is list else returnPP, "game_mode": gameMode, "length": bmap.hit_length, "stars": bmap.starsStd if bmap.starsStd is not None else bmap.difficultyrating, "ar": bmap.diff_approach, "bpm": bmap.bpm, } # Set status code and message statusCode = 200 data["message"] = "ok" except exceptions.invalidArgumentsException: # Set error and message statusCode = 400 data["message"] = "missing required arguments" except exceptions.invalidBeatmapException: statusCode = 400 data["message"] = "beatmap not found" except exceptions.beatmapTooLongException: statusCode = 400 data["message"] = "requested beatmap is too long" except exceptions.unsupportedGameModeException: statusCode = 400 data["message"] = "Unsupported gamemode" finally: # Add status code to data data["status"] = statusCode # Debug output log.debug(str(data)) # Send response self.write(json.dumps(data)) self.set_header("Content-Type", "application/json") self.set_status(statusCode)
def asyncGet(self): statusCode = 400 data = {"message": "unknown error"} try: # Check arguments if not requestsManager.checkArguments(self.request.arguments, ["b"]): raise exceptions.invalidArgumentsException(MODULE_NAME) # Get beatmap ID and make sure it's a valid number beatmapID = self.get_argument("b") if not beatmapID.isdigit(): raise exceptions.invalidArgumentsException(MODULE_NAME) # Get mods if "m" in self.request.arguments: modsEnum = self.get_argument("m") if not modsEnum.isdigit(): raise exceptions.invalidArgumentsException(MODULE_NAME) modsEnum = int(modsEnum) else: modsEnum = 0 # Get game mode if "g" in self.request.arguments: gameMode = self.get_argument("g") if not gameMode.isdigit(): raise exceptions.invalidArgumentsException(MODULE_NAME) gameMode = int(gameMode) else: gameMode = 0 # Get acc if "a" in self.request.arguments: accuracy = self.get_argument("a") try: accuracy = float(accuracy) except ValueError: raise exceptions.invalidArgumentsException(MODULE_NAME) else: accuracy = None # Print message log.info("Requested pp for beatmap {}".format(beatmapID)) # Get beatmap md5 from osuapi # TODO: Move this to beatmap object osuapiData = osuapiHelper.osuApiRequest("get_beatmaps", "b={}".format(beatmapID)) if int(beatmapID) > 100000000: raise exceptions.ppCustomBeatmap(MODULE_NAME) if osuapiData is None or "file_md5" not in osuapiData or "beatmapset_id" not in osuapiData: raise exceptions.invalidBeatmapException(MODULE_NAME) beatmapMd5 = osuapiData["file_md5"] beatmapSetID = osuapiData["beatmapset_id"] # Create beatmap object bmap = beatmap.beatmap(beatmapMd5, beatmapSetID) # Check beatmap length if bmap.hitLength > 900: raise exceptions.beatmapTooLongException(MODULE_NAME) returnPP = [] if gameMode == gameModes.STD and bmap.starsStd == 0: # Mode Specific beatmap, auto detect game mode if bmap.starsTaiko > 0: gameMode = gameModes.TAIKO if bmap.starsCtb > 0: gameMode = gameModes.CTB if bmap.starsMania > 0: gameMode = gameModes.MANIA # Calculate pp if gameMode in (gameModes.STD, gameModes.TAIKO): # Std pp if accuracy is None and modsEnum == 0: # Generic acc/nomod # Get cached pp values cachedPP = bmap.getCachedTillerinoPP() if (modsEnum & mods.RELAX): cachedPP = [0, 0, 0, 0] elif (modsEnum & mods.RELAX2): cachedPP = [0, 0, 0, 0] if cachedPP != [0, 0, 0, 0]: log.debug("Got cached pp.") returnPP = cachedPP else: log.debug( "Cached pp not found. Calculating pp with oppai..." ) # Cached pp not found, calculate them if gameMode == gameModes.STD and (modsEnum & mods.RELAX): oppai = relaxoppai.oppai(bmap, mods=modsEnum, tillerino=True) elif gameMode == gameModes.STD and (modsEnum & mods.RELAX2): oppai = relax2oppai.oppai(bmap, mods=modsEnum, tillerino=True) else: oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=True) returnPP = oppai.pp bmap.starsStd = oppai.stars if not (modsEnum & mods.RELAX) or (modsEnum & mods.RELAX2): # Cache values in DB log.debug("Saving cached pp...") if type(returnPP) == list and len(returnPP) == 4: bmap.saveCachedTillerinoPP(returnPP) else: # Specific accuracy/mods, calculate pp # Create oppai instance log.debug( "Specific request ({}%/{}). Calculating pp with oppai..." .format(accuracy, modsEnum)) if gameMode == gameModes.STD and (modsEnum & mods.RELAX): oppai = relaxoppai.oppai(bmap, mods=modsEnum, tillerino=True) elif gameMode == gameModes.STD and (modsEnum & mods.RELAX2): oppai = relax2oppai.oppai(bmap, mods=modsEnum, tillerino=True) else: oppai = rippoppai.oppai(bmap, mods=modsEnum, tillerino=True) bmap.starsStd = oppai.stars if accuracy is not None: returnPP = calculatePPFromAcc(oppai, accuracy) else: returnPP = oppai.pp else: raise exceptions.unsupportedGameModeException() # Data to return data = { "song_name": bmap.songName, "pp": [x for x in returnPP] if type(returnPP) is list else returnPP, "length": bmap.hitLength, "stars": bmap.starsStd, "ar": bmap.AR, "bpm": bmap.bpm, } # Set status code and message statusCode = 200 data["message"] = "ok" except exceptions.invalidArgumentsException: # Set error and message statusCode = 400 data["message"] = "missing required arguments" except exceptions.ppCustomBeatmap: statusCode = 400 data[ "message"] = "Custom map does not supported pp calculating yet" except exceptions.invalidBeatmapException: statusCode = 400 data["message"] = "beatmap not found" except exceptions.beatmapTooLongException: statusCode = 400 data["message"] = "requested beatmap is too long" except exceptions.unsupportedGameModeException: statusCode = 400 data["message"] = "Unsupported gamemode" finally: # Add status code to data data["status"] = statusCode # Debug output log.debug(str(data)) # Send response #self.clear() self.write(json.dumps(data)) self.set_header("Content-Type", "application/json") self.set_status(statusCode)
def asyncPost(self): statusCode = 400 data = {"message": "unknown error"} try: # Check arguments if not requestsManager.checkArguments(self.request.arguments, ["refresh"]): raise exceptions.invalidArgumentsException(MODULE_NAME) userID = 0 gameMode = 0 outputType = 'dap_rank' if 'u' in self.request.arguments: userID = int(self.get_argument('u')) if 'm' in self.request.arguments: gameMode = int(self.get_argument('m')) if 'out' in self.request.arguments: if self.get_argument('out').lower() in ('page', ): outputType = self.get_argument('out') # Get beatmap set data from osu api data["maps"] = [] refresh = int(self.get_argument("refresh")) apiResponse = [] if refresh == 1: log.debug("Forced refresh") if 'sid' in self.request.arguments: beatmapSetID = self.get_argument("sid") apiResponse = osuapiHelper.osuApiRequest( "get_beatmaps", "s={}".format(beatmapSetID), False) elif 'bid' in self.request.arguments: beatmapID = self.get_argument("bid") apiResponse = osuapiHelper.osuApiRequest( "get_beatmaps", "b={}".format(beatmapID), False) else: raise exceptions.invalidArgumentsException(MODULE_NAME) if len(apiResponse) == 0: raise exceptions.invalidBeatmapException forcefulStop = False for b in apiResponse: bm = glob.db.fetch( 'select beatmap_id id, beatmap_md5 md5 from beatmaps where beatmap_id = %s', [b['beatmap_id']]) if bm is None and not eligible.tryLoadBeatmap(userID): forcefulStop = 'beatmapNotice2' if bm is not None and bm['md5'] != b[ 'file_md5'] and not eligible.tryLoadBeatmap(userID): forcefulStop = 'beatmapNotice3' if forcefulStop: break if forcefulStop: params = { "k": glob.conf.config["server"]["apikey"], "to": userID, "code": forcefulStop, 'force': 1 } requests.get("{}/api/v1/specialNotice?{}".format( glob.conf.config["server"]["banchourl"], urlencode(params))) raise exceptions.invalidBeatmapException def processOutput(t, b, **x): if outputType == 'page': if b.creatorSrc == 'datenshi_id': ownerLink = "https://osu.troke.id/u/{}" else: ownerLink = "https://osu.ppy.sh/users/{}" if b.mapperSrc == 'datenshi_id': mapperLink = "https://osu.troke.id/u/{}" else: mapperLink = "https://osu.ppy.sh/users/{}" mapStat = b.stats.get(b.gameMode or gameMode, 0) mapDiff = { 'hp': mapStat.hp, 'od': mapStat.od, 'cs': mapStat.cs, 'ar': mapStat.ar, 'star': mapStat.rating, } if b.gameMode == 1: del mapDiff['cs'], mapDiff['ar'] elif b.gameMode == 2: del mapDiff['cs'] elif b.gameMode == 3: del mapDiff['ar'] return { 'artist': b.artist, 'title': b.title, 'owner': b.creatorName, 'owner_link': ownerLink.format(b.creatorID), 'mapper': b.mapperName or b.creatorName, 'mapper_link': mapperLink.format(b.mapperID) if b.mapperName else ownerLink.format(b.creatorID), 'source': b.source, 'version': b.difficultyName, 'difficulty': mapDiff, 'length': { 'drain': mapStat.actual_length_drain, 'total': mapStat.actual_length_total, }, 'bpm': b.bpm, } else: return { "id": b.beatmapID, "mode": b.mode, "baseName": "{} - {}".format(b.artist, b.title), "diffName": b.difficultyName, "status": b.rankedStatus, "frozen": b.rankedStatusFrozen, "pp": x['pp'], "request": x['request'] } # Loop through all beatmaps in this set and save them in db for i in apiResponse: log.debug("Saving beatmap {} in db".format(i["file_md5"])) bmap = beatmap.beatmap(i["file_md5"], int(i["beatmapset_id"]), refresh=refresh) if outputType == 'out' and bmap.takendown and not bmap.userHaveAccess( userID): continue pp = glob.db.fetch( "SELECT pp_100 FROM beatmaps WHERE beatmap_id = %s LIMIT 1", [bmap.beatmapID]) if pp is None: pp = 0 else: pp = pp["pp_100"] reqData = glob.db.fetch( "SELECT userid as user_id, blacklisted as bad, hidden as done FROM rank_requests WHERE (`type` = 'b' and bid = %s) or (`type` = 's' and bid = %s)", [bmap.beatmapID, bmap.beatmapSetID]) data["maps"].append( processOutput(outputType, bmap, pp=pp, request=reqData)) # Set status code and message statusCode = 200 data["message"] = "ok" except exceptions.invalidArgumentsException: # Set error and message statusCode = 400 data["message"] = "missing required arguments" except exceptions.invalidBeatmapException: statusCode = 400 data["message"] = "beatmap not found from osu!api." finally: # Add status code to data data["status"] = statusCode # Send response self.write(json.dumps(data)) self.set_header("Content-Type", "application/json") #self.add_header("Access-Control-Allow-Origin", "*") self.set_status(statusCode)
def autorankCheck(beatmap, recursive=True): # No autorank check for frozen maps if beatmap.rankedStatusFrozen not in (0, 3): return # No autorank check for already ranked beatmap # Only handle this if the map is ranked by autorank. if beatmap.rankedStatusFrozen != 3 and beatmap.rankedStatus >= 2: return # Only check if the map is autorankable. if not autorankFlagOK(beatmap.beatmapID): return # Check recursiveness if recursive: beatmap_id_query = glob.db.fetchAll( 'select beatmap_md5, beatmap_id from beatmaps where beatmapset_id = %s', [beatmap.beatmapSetID]) for beatmap_id_data in beatmap_id_query: # Skip self. if int(beatmap_id_data['beatmap_id']) == beatmap.beatmapID: continue siblingmap = bm.beatmap(None, beatmap.beatmapSetID) siblingmap.setData(beatmap_id_data['beatmap_md5'], beatmap.beatmapSetID, True) # Not checking maps with non updated date. #if recursive: #log.info(f"Checking {beatmap.artist} - {beatmap.title} for autorank eligiblity.") # log.info(f"Checking {beatmap.songName} for autorank eligiblity.") obtainDateTime = lambda t: datetime.datetime.strptime( t, "%Y-%m-%d %H:%M:%S") obtainUnixClock = lambda t: int(time.mktime(t.timetuple())) if beatmap.updateDate == 0: log.info(f"Updating {beatmap.fileMD5} data") data = osuapiHelper.osuApiRequest('get_beatmaps', 'h={}'.format(beatmap.fileMD5)) if not data: return dateTouch = obtainDateTime(data['last_update']) beatmap.updateDate = obtainUnixClock(dateTouch) else: dateTouch = datetime.datetime.fromtimestamp(beatmap.updateDate) dateNow = datetime.datetime.today() dateQualify = dateTouch + datetime.timedelta(days=GRAVEYARD_DAYS - QUALIFIED_DAYS) dateRanked = dateTouch + datetime.timedelta(days=GRAVEYARD_DAYS) forLove = autorankFlagForLove(beatmap.beatmapID) rankStatus = beatmap.rankedStatus needWipe = False if dateNow >= dateRanked: needWipe = rankStatus == rankedStatuses.QUALIFIED if forLove: log.debug(f"Considering {beatmap.fileMD5} to be loved") beatmap.rankedStatus = rankedStatuses.LOVED else: log.debug(f"Considering {beatmap.fileMD5} on ranking") beatmap.rankedStatus = rankedStatuses.RANKED beatmap.rankedStatusFrozen = 3 elif dateNow >= dateQualify and not forLove: log.debug(f"Considering {beatmap.fileMD5} for qualified") beatmap.rankedStatus = rankedStatuses.QUALIFIED beatmap.rankedStatusFrozen = 3 else: needWipe = rankStatus >= rankedStatuses.RANKED beatmap.rankedStatus = rankedStatuses.PENDING beatmap.rankedStatusFrozen = 0 if rankStatus != beatmap.rankedStatus: dbBeatmap = glob.db.fetch( 'select ranked_status_freezed as f from beatmaps where beatmap_md5 = %s', [beatmap.fileMD5]) oldFreeze = 0 if dbBeatmap is not None: oldFreeze = dbBeatmap['f'] glob.db.execute( 'update beatmaps set ranked_status_freezed = 0 where beatmap_md5 = %s', [beatmap.fileMD5]) if oldFreeze != beatmap.rankedStatusFrozen and recursive: autorankAnnounce(beatmap) glob.db.execute( 'update beatmaps set rankedby = %s where beatmap_md5 = %s', [autorankUserID(beatmap.beatmapID), beatmap.fileMD5]) if needWipe: log.info(f"Wiping {beatmap.fileMD5} leaderboard") beatmap.clearLeaderboard() pass