async def update_action(user: Player, p: bytes) -> None: d = reader.handle_packet( p, ( ("actionid", osuTypes.u8), ("base_info", osuTypes.string), ("md5", osuTypes.string), ("mods", osuTypes.u32), ("mode", osuTypes.u8), ("mid", osuTypes.i32), ), ) if d["actionid"] == 0 and d["mods"] & Mods.RELAX: d["base_info"] = "on Relax" elif d["actionid"] == 0 and d["mods"] & Mods.AUTOPILOT: d["base_info"] = "on Autopilot" user.action = d["actionid"] user.base_info = d["base_info"] user.map_md5 = d["md5"] user.mods = d["mods"] m = lbModes(d["mode"], d["mods"]) user.mode = m.value user.mode_vn = m.as_vn user.map_id = d["mid"] if d["actionid"] == 2: user.base_info += f" +{(Mods(user.mods))!r}" # ugly and i dont care! if not user.restricted: glob.players.enqueue(writer.userStats(user))
async def update_action(user: Player, p: bytes) -> None: d = reader.handle_packet(p, ( ('actionid', osuTypes.u8), ('info', osuTypes.string), ('md5', osuTypes.string), ('mods', osuTypes.u32), ('mode', osuTypes.u8), ('mid', osuTypes.i32) )) if d['actionid'] == 0 and d['mods'] & Mods.RELAX: d['info'] = 'on Relax' elif d['actionid'] == 0 and d['mods'] & Mods.AUTOPILOT: d['info'] = 'on Autopilot' user.action = d['actionid'] user.info = d['info'] user.map_md5 = d['md5'] user.mods = d['mods'] m = lbModes(d['mode'], d['mods']) user.mode = m.value user.mode_vn = m.as_vn user.map_id = d['mid'] if d['actionid'] == 2: user.info += f' +{(Mods(user.mods))!r}' # ugly and i dont care! if not user.restricted: glob.players.enqueue(writer.userStats(user))
async def mostPlayed(req: Request) -> Union[tuple, dict]: args = req.args uid = int(args.get('id', 0)) username = args.get('username', None) m = int(args.get('mode', 0)) r = int(args.get('rx', 0)) limit = int(args.get('limit', 5)) if r == 0: rx = Mods.NOMOD elif r == 1: rx = Mods.RELAX elif r == 2: rx = Mods.AUTOPILOT mode = lbModes(m, rx) if not uid and not username: return (400, { 'message': 'please provide either a username or user id!' }) if not uid: uid = await glob.db.fetchval('SELECT id FROM users WHERE name = %s', [username]) if not (user := await glob.players.get(id=uid, sql=True)): return (400, {'message': "user couldn't be found!"})
async def from_sql( cls, sid: int, table: str, sort: str, t: int, ensure: bool = False, ) -> Optional["Score"]: score = await glob.db.fetchrow(f"SELECT * FROM {table} WHERE id = %s", [sid]) if not score: return self = cls() self.id = sid self.map = await Beatmap.from_md5(score["md5"]) if not self.map: return # ? self.user = await glob.players.get(id=score["uid"], sql=ensure) if not self.user: return self self.pp = score["pp"] self.score = score["score"] self.combo = score["combo"] self.mods = Mods(score["mods"]) self.acc = score["acc"] self.n300 = score["n300"] self.n100 = score["n100"] self.n50 = score["n50"] self.miss = score["miss"] self.geki = score["geki"] self.katu = score["katu"] self.grade = score["grade"] self.fc = score["fc"] self.status = scoreStatuses(score["status"]) self.mode = lbModes(score["mode"], self.mods) self.time = score["time"] self.passed = self.status.value != 0 if not self.user.restricted: self.rank = await self.calc_lb(table, sort, t) else: self.rank = 0 self.osuver = score["osuver"] return self
async def from_sql(cls, sid: int, table: str, sort: str, t: int, ensure: bool = False) -> Optional['Score']: score = await glob.db.fetchrow(f'SELECT * FROM {table} WHERE id = %s', [sid]) if not score: return self = cls() self.id = sid self.map = await Beatmap.from_md5(score['md5']) if not self.map: return # ? self.user = await glob.players.get(id=score['uid'], sql=ensure) if not self.user: return self self.pp = score['pp'] self.score = score['score'] self.combo = score['combo'] self.mods = Mods(score['mods']) self.acc = score['acc'] self.n300 = score['n300'] self.n100 = score['n100'] self.n50 = score['n50'] self.miss = score['miss'] self.geki = score['geki'] self.katu = score['katu'] self.grade = score['grade'] self.fc = score['fc'] self.status = scoreStatuses(score['status']) self.mode = lbModes(score['mode'].as_vn, self.mods) self.time = score['time'] self.passed = self.status.value != 0 if not self.user.restricted: self.rank = await self.calc_lb(table, sort, t) else: self.rank = 0 self.osuver = score['osuver'] return self
async def mostPlayed(req: Request) -> Union[tuple, dict]: args = req.args uid = int(args.get("id", 0)) username = make_safe(args.get("username", None)) m = int(args.get("mode", 0)) r = int(args.get("rx", 0)) limit = int(args.get("limit", 6)) if not 0 <= r < 3: return (400, {"message": "invalid relax value (must be 0, 1 or 2)"}) if r == 0: rx = Mods.NOMOD elif r == 1: rx = Mods.RELAX elif r == 2: rx = Mods.AUTOPILOT mode = lbModes(m, rx) if not uid and not username: return (400, { "message": "please provide either a username or user id!" }) if not uid: uid = await glob.db.fetchval( "SELECT id FROM users WHERE safe_name = %s", [username], ) if not (user := await glob.players.get(id=uid, sql=True)): return (400, {"message": "user couldn't be found!"})
class Score: __slots__ = ( "id", "map", "user", "score", "acc", "n300", "n100", "n50", "miss", "geki", "katu", "grade", "mods", "readable_mods", "combo", "mode", "rank", "pp", "sr", "fc", "passed", "status", "time", "old_best", "osuver", "ur", ) def __init__(self) -> None: self.id: Optional[int] = None self.map: Optional[Beatmap] = None self.user: Optional[Player] = None self.score: Optional[int] = None self.acc: Optional[float] = None self.n300: Optional[int] = None self.n100: Optional[int] = None self.n50: Optional[int] = None self.miss: Optional[int] = None self.geki: Optional[int] = None self.katu: Optional[int] = None self.grade: Optional[Grade] = None self.mods: Optional[Mods] = None self.readable_mods: Optional[str] = None self.combo: Optional[int] = None self.mode: Optional[osuModes] = None self.rank: Optional[int] = None self.pp: Optional[float] = None self.sr: Optional[float] = None self.fc: Optional[bool] = None self.passed: Optional[bool] = None self.status: Optional[scoreStatuses] = None self.time: Optional[int] = None self.old_best: Optional[Score] = None self.osuver: Optional[float] = None self.ur: Optional[float] = None async def format(self) -> str: msg = ( f"{self.user.name} | {self.map.name} +{self.readable_mods} {self.acc:.2f}% " f'{"FC" if not self.miss else f"{self.miss}xMiss"} {self.pp:,.0f}pp' ) if self.miss: fc_score = copy.copy(self) fc_score.fc = True fc_score.combo = 0 # oppai will take max combo pp, _ = await fc_score.calc_pp(self.mode.as_vn) msg += f" (~{round(pp):,}pp for FC)" if self.mode.value == 0 and self.ur: msg += f" | {self.ur:.2f} (cv)UR" return msg @classmethod async def from_sql( cls, sid: int, table: str, sort: str, t: int, ensure: bool = False, ) -> Optional["Score"]: score = await glob.db.fetchrow(f"SELECT * FROM {table} WHERE id = %s", [sid]) if not score: return self = cls() self.id = sid self.map = await Beatmap.from_md5(score["md5"]) if not self.map: return # ? self.user = await glob.players.get(id=score["uid"], sql=ensure) if not self.user: return self self.pp = score["pp"] self.score = score["score"] self.combo = score["combo"] self.mods = Mods(score["mods"]) self.acc = score["acc"] self.n300 = score["n300"] self.n100 = score["n100"] self.n50 = score["n50"] self.miss = score["miss"] self.geki = score["geki"] self.katu = score["katu"] self.grade = score["grade"] self.fc = score["fc"] self.status = scoreStatuses(score["status"]) self.mode = lbModes(score["mode"], self.mods) self.time = score["time"] self.passed = self.status.value != 0 if not self.user.restricted: self.rank = await self.calc_lb(table, sort, t) else: self.rank = 0 self.osuver = score["osuver"] return self @classmethod async def from_submission( cls, base: str, iv: str, pw: str, ver: str, ) -> Optional["Score"]: rijndael = RijndaelCbc( # much better f**k one liners key=f"osu!-scoreburgr---------{ver}".encode(), iv=b64decode(iv), padding=Pkcs7Padding(32), block_size=32, ) data = rijndael.decrypt(b64decode(base)).decode().split(":") self = cls() self.map = await Beatmap.from_md5(data[0]) if (u := await glob.players.get(name=data[1].rstrip())) and u.pw == pw: self.user = u if not self.user: return self # even if user isnt found, may be related to connection and we want to tell the client to retry if not self.map: return # ?? # i wanted to make everything be set in the same order as init but some require all score info to exist first so sadly not :c self.score = int(data[9]) self.n300 = int(data[3]) self.n100 = int(data[4]) self.n50 = int(data[5]) self.miss = int(data[8]) self.geki = int(data[6]) self.katu = int(data[7]) self.mods = Mods(int(data[13])) self.readable_mods = repr(Mods(int(data[13]))) self.combo = int(data[10]) self.mode = lbModes(int(data[15]), self.mods) self.fc = data[11] == "True" # WHY IS OSU GIVING STRING FOR BOOL!!!!!! self.passed = data[14] == "True" # AGAIN OSU WHY!!!! self.time = round(time.time()) # have to add round cast cus it gives float smh self.grade = data[12] if self.passed else "F" await self.calc_info() self.pp, self.sr = await self.calc_pp(self.mode.as_vn) await self.score_order() if self.user.restricted: self.rank = 0 self.osuver = float(re.sub("[^0-9]", "", ver)) # lol return self
f'{_map["status"]}|{"|".join(grades)}') return '\n'.join(ret).encode() @web.route("/web/osu-osz2-getscores.php") async def getMapScores(request: Request) -> bytes: args = request.args if not await auth(args['us'], args['ha'], request): return b'' if (md5 := args['c']) in glob.cache['unsub']: return b'-1|false' # tell client map is unsub xd mods = int(args['mods']) mode = lbModes(int(args['m']), mods) lbm = int(args['v']) player = request.extras.get('player') if mode.value != player.mode or mods != player.mods: player.mode = mode.value player.mode_vn = mode.as_vn player.mods = mods if not player.restricted: glob.players.enqueue(writer.userStats(player)) bmap = await Beatmap.from_md5(md5) if not bmap:
async def getLb(request: Request) -> list: args = request.args mode = int(args.get("mode", 0)) rx = int(args.get("rx", 0)) search = make_safe(args.get("u", None)) limit = int(args.get("limit", 50)) page = int(args.get("p", 0)) country = args.get("country", None) if rx == 0: rx = Mods.NOMOD elif rx == 1: rx = Mods.RELAX elif rx == 2: rx = Mods.AUTOPILOT lb_mode = lbModes(mode, rx) lb_str = f"asahi:leaderboard:{lb_mode.name}" if country: lb_str += f":{country.upper()}" lb = [int(u) for u in await glob.redis.zrevrangebyscore(lb_str)] # limit amount of users to return lb = lb[:limit] if page: offset = limit * page # ? lb = lb[offset:] ret = [] # TODO: clean this? if not search: for rank, uid in enumerate(lb): info = await glob.db.fetchrow( "SELECT users.name, users.country, stats.pp_{0} pp, stats.acc_{0} acc, stats.pc_{0} pc FROM users " "LEFT OUTER JOIN stats ON stats.id = users.id WHERE users.id = %s" .format(lb_mode.name, ), [uid], ) db_grades = await glob.db.fetchrow( 'SELECT SUM(grade IN ("X", "XH")) AS ss, ' 'SUM(grade IN ("S", "SH")) AS s, ' 'SUM(grade = "A") AS a FROM {0} WHERE uid = %s AND mode = %s'. format(lb_mode.table, ), [uid, mode], ) ret.append( { "rank": rank + 1, "userid": uid, "name": info["name"], "country": info["country"], "pp": info["pp"], "acc": info["acc"], "playcount": info["pc"], "grades": {key: int(val or 0) for key, val in db_grades.items()}, }, ) return ret users = await glob.db.fetch( f"SELECT users.name, users.id, users.country, stats.pp_{lb_mode.name} pp, stats.acc_{lb_mode.name} acc, stats.pc_{lb_mode.name} pc " f"FROM users LEFT OUTER JOIN stats ON stats.id = users.id WHERE users.safe_name LIKE %s", [f"{search}%"], ) for info in users: if info["id"] not in lb: continue db_grades = await glob.db.fetchrow( 'SELECT SUM(grade IN ("SS", "SSH")) AS ss, ' 'SUM(grade IN ("S", "SH")) AS s, ' 'SUM(grade = "A") AS a FROM {0} WHERE uid = %s AND mode = %s'. format(lb_mode.table, ), [info["id"], mode], ) ret.append( { "rank": lb.index(info["id"]) + 1, "userid": info["id"], "name": info["name"], "country": info["country"], "pp": info["pp"], "acc": info["acc"], "playcount": info["pc"], "grades": {key: int(val) for key, val in db_grades.items()}, }, ) return ret
) if not (user := await glob.players.get(id=id, sql=True)): return (400, {"message": "user couldn't be found!"}) if user.priv & Privileges.Disallowed: return (400, {"message": "user is restricted/banned!"}) if rx == 0: rx = Mods.NOMOD elif rx == 1: rx = Mods.RELAX elif rx == 2: rx = Mods.AUTOPILOT mode = lbModes(m, rx) info = { "id": user.id, "name": user.name, "country": user.country_iso, "priv": user.priv, "clan": { "id": user.clan.id, "name": user.clan.name, "tag": user.clan.tag } if user.clan else None, } return {"info": info, "stats": user.stats[mode.value]}
class Score: __slots__ = ('id', 'map', 'user', 'score', 'acc', 'n300', 'n100', 'n50', 'miss', 'geki', 'katu', 'grade', 'mods', 'readable_mods', 'combo', 'mode', 'rank', 'pp', 'sr', 'fc', 'passed', 'status', 'time', 'old_best', 'osuver', 'ur') def __init__(self) -> None: self.id: Optional[int] = None self.map: Optional[Beatmap] = None self.user: Optional[Player] = None self.score: Optional[int] = None self.acc: Optional[float] = None self.n300: Optional[int] = None self.n100: Optional[int] = None self.n50: Optional[int] = None self.miss: Optional[int] = None self.geki: Optional[int] = None self.katu: Optional[int] = None self.grade: Optional[Grade] = None self.mods: Optional[Mods] = None self.readable_mods: Optional[str] = None self.combo: Optional[int] = None self.mode: Optional[osuModes] = None self.rank: Optional[int] = None self.pp: Optional[float] = None self.sr: Optional[float] = None self.fc: Optional[bool] = None self.passed: Optional[bool] = None self.status: Optional[scoreStatuses] = None self.time: Optional[int] = None self.old_best: Optional[Score] = None self.osuver: Optional[int] = None self.ur: Optional[float] = None async def format(self) -> str: msg = ( f'{self.user.name} | {self.map.name} +{self.readable_mods} {self.acc:.2f}% ' f'{"FC" if not self.miss else f"{self.miss}xMiss"} {self.pp:,.0f}pp' ) if self.miss: fc_score = copy.copy(self) fc_score.fc = True fc_score.combo = 0 # oppai will take max combo pp, _ = await fc_score.calc_pp(self.mode.as_vn) msg += f' (~{round(pp):,}pp for FC)' if self.mode.value == 0 and self.ur: msg += f' | {self.ur:.2f} (cv)UR' return msg @classmethod async def from_sql(cls, sid: int, table: str, sort: str, t: int, ensure: bool = False) -> Optional['Score']: score = await glob.db.fetchrow(f'SELECT * FROM {table} WHERE id = %s', [sid]) if not score: return self = cls() self.id = sid self.map = await Beatmap.from_md5(score['md5']) if not self.map: return # ? self.user = await glob.players.get(id=score['uid'], sql=ensure) if not self.user: return self self.pp = score['pp'] self.score = score['score'] self.combo = score['combo'] self.mods = Mods(score['mods']) self.acc = score['acc'] self.n300 = score['n300'] self.n100 = score['n100'] self.n50 = score['n50'] self.miss = score['miss'] self.geki = score['geki'] self.katu = score['katu'] self.grade = score['grade'] self.fc = score['fc'] self.status = scoreStatuses(score['status']) self.mode = lbModes(score['mode'].as_vn, self.mods) self.time = score['time'] self.passed = self.status.value != 0 if not self.user.restricted: self.rank = await self.calc_lb(table, sort, t) else: self.rank = 0 self.osuver = score['osuver'] return self @classmethod async def from_submission(cls, base: str, iv: str, pw: str, ver: str) -> Optional['Score']: rijndael = RijndaelCbc( # much better f**k one liners key=f'osu!-scoreburgr---------{ver}', iv=b64decode(iv), padding=Pkcs7Padding(32), block_size=32) data = rijndael.decrypt(b64decode(base)).decode().split(':') self = cls() self.map = await Beatmap.from_md5(data[0]) if (u := await glob.players.get(name=data[1].rstrip())) and u.pw == pw: self.user = u if not self.user: return self # even if user isnt found, may be related to connection and we want to tell the client to retry if not self.map: return # ?? # i wanted to make everything be set in the same order as init but some require all score info to exist first so sadly not :c self.score = int(data[9]) self.n300 = int(data[3]) self.n100 = int(data[4]) self.n50 = int(data[5]) self.miss = int(data[8]) self.geki = int(data[6]) self.katu = int(data[7]) self.mods = Mods(int(data[13])) self.readable_mods = repr(Mods(int(data[13]))) self.combo = int(data[10]) self.mode = lbModes(int(data[15]), self.mods) self.fc = data[11] == 'True' # WHY IS OSU GIVING STRING FOR BOOL!!!!!! self.passed = data[14] == 'True' # AGAIN OSU WHY!!!! self.time = round( time.time()) # have to add round cast cus it gives float smh self.grade = data[12] if self.passed else 'F' await self.calc_info() self.pp, self.sr = await self.calc_pp(self.mode.as_vn) await self.score_order() if self.user.restricted: self.rank = 0 self.osuver = float(re.sub("[^0-9]", "", ver)) # lol return self
if user.priv & Privileges.Disallowed: return await ctx.send("This user is banned/restricted!") m = 0 if "-m" in args: m = int(args[args.index("-m") + 1]) if "-rx" in args: rx = Mods.RELAX elif "-ap" in args: rx = Mods.AUTOPILOT else: rx = Mods.NOMOD mode = lbModes(m, rx) stats = user.stats[mode.value] embed = Embed(title="") embed.set_author( name= f"{mode!r} profile for {user.full_name} - {stats.pp}pp (#{stats.rank} // {user.country_iso.upper()}#{stats.country_rank})", url=f"https://{glob.config.domain}/u/{user.id}", icon_url=f"https://countryflags.io/{user.country_iso}/flat/64.png", ) embed.set_thumbnail(url=f"https://a.{glob.config.domain}/{user.id}") # TODO: finish embed lol return await ctx.send(embed=embed)
async def getLb(request: Request) -> list: args = request.args mode = int(args.get('mode', 0)) rx = int(args.get('rx', 0)) search = args.get('u', None) limit = int(args.get('limit', 50)) page = int(args.get('p', 0)) country = args.get('country', None) if rx == 0: rx = Mods.NOMOD elif rx == 1: rx = Mods.RELAX elif rx == 2: rx = Mods.AUTOPILOT lb_mode = lbModes(mode, rx) lb_str = f'asahi:leaderboard:{lb_mode.name}' if country: lb_str += f':{country.upper()}' lb = [int(u) for u in await glob.redis.zrangebyscore(lb_str)] lb.reverse() # redis returns backwards?? # limit amount of users to return lb = lb[:limit] if page: offset = limit * page # ? lb = lb[offset:] ret = [] # TODO: clean this? if not search: for rank, uid in enumerate(lb): info = await glob.db.fetchrow( 'SELECT users.name, users.country, stats.pp_{0} pp, stats.acc_{0} acc, stats.pc_{0} pc FROM users ' 'LEFT OUTER JOIN stats ON stats.id = users.id WHERE users.id = %s' .format(lb_mode.name), [uid]) db_grades = await glob.db.fetchrow( 'SELECT SUM(grade IN ("SS", "SSH")) AS ss, ' 'SUM(grade IN ("S", "SH")) AS s, ' 'SUM(grade = "A") AS a FROM {0} WHERE uid = %s AND mode = %s'. format(lb_mode.table), [uid, mode]) ret.append({ 'rank': rank + 1, 'userid': uid, 'name': info['name'], 'country': info['country'], 'pp': info['pp'], 'acc': info['acc'], 'playcount': info['pc'], 'grades': {key: int(val) for key, val in db_grades.items()} }) return ret users = await glob.db.fetch( f'SELECT users.name, users.id, users.country, stats.pp_{lb_mode.name} pp, stats.acc_{lb_mode.name} acc, stats.pc_{lb_mode.name} pc ' f'FROM users LEFT OUTER JOIN stats ON stats.id = users.id WHERE users.name LIKE %s', [f'{search}%']) for info in users: if info['id'] not in lb: continue db_grades = await glob.db.fetchrow( 'SELECT SUM(grade IN ("SS", "SSH")) AS ss, ' 'SUM(grade IN ("S", "SH")) AS s, ' 'SUM(grade = "A") AS a FROM {0} WHERE uid = %s AND mode = %s'. format(lb_mode.table), [info['id'], mode]) ret.append({ 'rank': lb.index(info['id']) + 1, 'userid': info['id'], 'name': info['name'], 'country': info['country'], 'pp': info['pp'], 'acc': info['acc'], 'playcount': info['pc'], 'grades': {key: int(val) for key, val in db_grades.items()} }) return ret
f'{_map["status"]}|{"|".join(grades)}', ) return "\n".join(ret).encode() @web.route("/web/osu-osz2-getscores.php") async def getMapScores(request: Request) -> bytes: args = request.args if not await auth(args["us"], args["ha"], request): return b"" if (md5 := args["c"]) in glob.cache["unsub"]: return b"-1|false" # tell client map is unsub xd mods = int(args["mods"]) mode = lbModes(int(args["m"]), mods) lbm = int(args["v"]) player = request.extras["player"] if mode.value != player.mode or mods != player.mods: player.mode = mode.value player.mode_vn = mode.as_vn player.mods = mods if not player.restricted: glob.players.enqueue(writer.userStats(player)) bmap = await Beatmap.from_md5(md5) if not bmap: