diff --git a/app/api/domains/cho.py b/app/api/domains/cho.py index 05a972211..da73bd74d 100644 --- a/app/api/domains/cho.py +++ b/app/api/domains/cho.py @@ -214,7 +214,7 @@ async def bancho_handler( ), ) - if player.restricted: + if player.is_restricted: # restricted users may only use certain packet handlers. packet_map = app.state.packets["restricted"] else: @@ -293,7 +293,7 @@ async def handle(self, player: Player) -> None: player.status.map_id = self.map_id # broadcast it to all online players. - if not player.restricted: + if not player.is_restricted: app.state.sessions.players.enqueue(app.packets.user_stats(player)) @@ -306,7 +306,7 @@ def __init__(self, reader: BanchoPacketReader) -> None: self.msg = reader.read_message() async def handle(self, player: Player) -> None: - if player.silenced: + if player.is_silenced: log(f"{player} sent a message while silenced.", Ansi.LYELLOW) return @@ -914,14 +914,14 @@ async def handle_osu_login_request( data += user_data - if not player.restricted: + if not player.is_restricted: # player is unrestricted, two way data for o in app.state.sessions.players: # enqueue us to them o.enqueue(user_data) # enqueue them to us. - if not o.restricted: + if not o.is_restricted: if o is app.state.sessions.bot: # optimization for bot since it's # the most frequently requested user @@ -1012,7 +1012,7 @@ async def handle_osu_login_request( app.state.sessions.players.append(player) if app.state.services.datadog: - if not player.restricted: + if not player.is_restricted: app.state.services.datadog.increment("bancho.online_players") time_taken = time.time() - login_time @@ -1127,7 +1127,7 @@ def __init__(self, reader: BanchoPacketReader) -> None: self.msg = reader.read_message() async def handle(self, player: Player) -> None: - if player.silenced: + if player.is_silenced: if app.settings.DEBUG: log(f"{player} tried to send a dm while silenced.", Ansi.LYELLOW) return @@ -1165,7 +1165,7 @@ async def handle(self, player: Player) -> None: log(f"{player} tried to message {target}, but they are blocking dms.") return - if target.silenced: + if target.is_silenced: # if target is silenced, inform player. player.enqueue(app.packets.target_silenced(target_name)) @@ -1353,7 +1353,7 @@ async def handle(self, player: Player) -> None: log(f"{player} tried to create a match with invalid data.", Ansi.LYELLOW) return - if player.restricted: + if player.is_restricted: player.enqueue( app.packets.match_join_fail() + app.packets.notification( @@ -1362,7 +1362,7 @@ async def handle(self, player: Player) -> None: ) return - if player.silenced: + if player.is_silenced: player.enqueue( app.packets.match_join_fail() + app.packets.notification( @@ -1431,7 +1431,7 @@ async def handle(self, player: Player) -> None: player.enqueue(app.packets.match_join_fail()) return - if player.restricted: + if player.is_restricted: player.enqueue( app.packets.match_join_fail() + app.packets.notification( @@ -1440,7 +1440,7 @@ async def handle(self, player: Player) -> None: ) return - if player.silenced: + if player.is_silenced: player.enqueue( app.packets.match_join_fail() + app.packets.notification( diff --git a/app/api/domains/osu.py b/app/api/domains/osu.py index b533e956b..33a80a23c 100644 --- a/app/api/domains/osu.py +++ b/app/api/domains/osu.py @@ -666,7 +666,7 @@ async def osuSubmitModularSelector( score.player.status.mods = score.mods score.player.status.mode = score.mode - if not score.player.restricted: + if not score.player.is_restricted: app.state.sessions.players.enqueue(app.packets.user_stats(score.player)) # hold a lock around (check if submitted, submission) to ensure no duplicates @@ -729,7 +729,7 @@ async def osuSubmitModularSelector( ), ) - if score.rank == 1 and not score.player.restricted: + if score.rank == 1 and not score.player.is_restricted: announce_chan = app.state.sessions.channels.get_by_name("#announce") ann = [ @@ -826,7 +826,7 @@ async def osuSubmitModularSelector( else: log(f"{score.player} submitted a score without a replay!", Ansi.LRED) - if not score.player.restricted: + if not score.player.is_restricted: await score.player.restrict( admin=app.state.sessions.bot, reason="submitted score with no replay", @@ -946,7 +946,7 @@ async def osuSubmitModularSelector( pp=stats_updates.get("pp", UNSET), ) - if not score.player.restricted: + if not score.player.is_restricted: # enqueue new stats info to all other users app.state.sessions.players.enqueue(app.packets.user_stats(score.player)) @@ -974,7 +974,7 @@ async def osuSubmitModularSelector( response = b"error: no" else: # construct and send achievements & ranking charts to the client - if score.bmap.awards_ranked_pp and not score.player.restricted: + if score.bmap.awards_ranked_pp and not score.player.is_restricted: unlocked_achievements: list[Achievement] = [] server_achievements = await achievements_usecases.fetch_many() @@ -1287,7 +1287,7 @@ async def getScores( player.status.mods = mods player.status.mode = mode - if not player.restricted: + if not player.is_restricted: app.state.sessions.players.enqueue(app.packets.user_stats(player)) scoring_metric: Literal["pp", "score"] = ( diff --git a/app/commands.py b/app/commands.py index 262ae3765..2d343560b 100644 --- a/app/commands.py +++ b/app/commands.py @@ -812,7 +812,7 @@ async def unsilence(ctx: Context) -> str | None: if not target: return f'"{ctx.args[0]}" not found.' - if not target.silenced: + if not target.is_silenced: return f"{target} is not silenced." if target.priv & Privileges.STAFF and not ctx.player.priv & Privileges.DEVELOPER: @@ -877,7 +877,7 @@ async def user(ctx: Context) -> str | None: f"Logged in: {timeago.format(player.login_time)}", f"Last server interaction: {timeago.format(player.last_recv_time)}", f"osu! build: {osu_version} | Tourney: {player.is_tourney_client}", - f"Silenced: {player.silenced} | Spectating: {player.spectating}", + f"Silenced: {player.is_silenced} | Spectating: {player.spectating}", f"Last /np: {last_np}", f"Recent score: {player.recent_score}", f"Match: {player.match}", @@ -900,7 +900,7 @@ async def restrict(ctx: Context) -> str | None: if target.priv & Privileges.STAFF and not ctx.player.priv & Privileges.DEVELOPER: return "Only developers can manage staff members." - if target.restricted: + if target.is_restricted: return f"{target} is already restricted!" reason = " ".join(ctx.args[1:]) @@ -931,7 +931,7 @@ async def unrestrict(ctx: Context) -> str | None: if target.priv & Privileges.STAFF and not ctx.player.priv & Privileges.DEVELOPER: return "Only developers can manage staff members." - if not target.restricted: + if not target.is_restricted: return f"{target} is not restricted!" reason = " ".join(ctx.args[1:]) @@ -1137,19 +1137,24 @@ async def givedonator(ctx: Context) -> str | None: if not target: return "Could not find user." + new_donor_end = time.time() + + # keep existing time + if target.has_donator: + new_donor_end = target.donor_end + + # add new time timespan = timeparse(ctx.args[1]) if not timespan: return "Invalid timespan." + new_donor_end += timespan - if target.donor_end < time.time(): - timespan += int(time.time()) - else: - timespan += target.donor_end - - target.donor_end = timespan - await app.state.services.database.execute( - "UPDATE users SET donor_end = :end WHERE id = :user_id", - {"end": timespan, "user_id": target.id}, + # persist to db + new_donor_end = int(new_donor_end) + target.donor_end = new_donor_end + await players_repo.update( + id=target.id, + donor_end=new_donor_end, ) await target.add_privs(Privileges.SUPPORTER) @@ -1903,12 +1908,12 @@ async def mp_loadpool(ctx: Context, match: Match) -> str | None: return "Could not find a pool by that name!" if ( - match.tourney_pool is not None - and match.tourney_pool["id"] == tourney_pool["id"] + match.tourney_pool_id is not None + and match.tourney_pool_id == tourney_pool["id"] ): return f"{tourney_pool['name']} already selected!" - match.tourney_pool = tourney_pool + match.tourney_pool_id = tourney_pool["id"] return f"{tourney_pool['name']} selected." @@ -1922,10 +1927,10 @@ async def mp_unloadpool(ctx: Context, match: Match) -> str | None: if ctx.player is not match.host: return "Only available to the host." - if not match.tourney_pool: + if not match.tourney_pool_id: return "No mappool currently selected!" - match.tourney_pool = None + match.tourney_pool_id = None return "Mappool unloaded." @@ -1936,7 +1941,7 @@ async def mp_ban(ctx: Context, match: Match) -> str | None: if len(ctx.args) != 1: return "Invalid syntax: !mp ban " - if not match.tourney_pool: + if not match.tourney_pool_id: return "No pool currently selected!" mods_slot = ctx.args[0] @@ -1951,7 +1956,7 @@ async def mp_ban(ctx: Context, match: Match) -> str | None: slot = int(r_match[2]) map_pick = await tourney_pool_maps_repo.fetch_by_pool_and_pick( - pool_id=match.tourney_pool["id"], + pool_id=match.tourney_pool_id, mods=mods, slot=slot, ) @@ -1972,7 +1977,7 @@ async def mp_unban(ctx: Context, match: Match) -> str | None: if len(ctx.args) != 1: return "Invalid syntax: !mp unban " - if not match.tourney_pool: + if not match.tourney_pool_id: return "No pool currently selected!" mods_slot = ctx.args[0] @@ -1987,7 +1992,7 @@ async def mp_unban(ctx: Context, match: Match) -> str | None: slot = int(r_match[2]) map_pick = await tourney_pool_maps_repo.fetch_by_pool_and_pick( - pool_id=match.tourney_pool["id"], + pool_id=match.tourney_pool_id, mods=mods, slot=slot, ) @@ -2008,7 +2013,7 @@ async def mp_pick(ctx: Context, match: Match) -> str | None: if len(ctx.args) != 1: return "Invalid syntax: !mp pick " - if not match.tourney_pool: + if not match.tourney_pool_id: return "No pool currently loaded!" mods_slot = ctx.args[0] @@ -2023,7 +2028,7 @@ async def mp_pick(ctx: Context, match: Match) -> str | None: slot = int(r_match[2]) map_pick = await tourney_pool_maps_repo.fetch_by_pool_and_pick( - pool_id=match.tourney_pool["id"], + pool_id=match.tourney_pool_id, mods=mods, slot=slot, ) diff --git a/app/objects/match.py b/app/objects/match.py index 69b4b1674..e8052dca9 100644 --- a/app/objects/match.py +++ b/app/objects/match.py @@ -188,7 +188,7 @@ def __init__( self.starting: StartingTimers | None = None self.seed = seed # used for mania random mod - self.tourney_pool: TourneyPool | None = None + self.tourney_pool_id: int | None = None # scrimmage stuff self.is_scrimming = False diff --git a/app/objects/player.py b/app/objects/player.py index 5a5c4ca59..3f2f5f002 100644 --- a/app/objects/player.py +++ b/app/objects/player.py @@ -333,10 +333,14 @@ def remaining_silence(self) -> int: return max(0, int(self.silence_end - time.time())) @property - def silenced(self) -> bool: + def is_silenced(self) -> bool: """Whether or not the player is silenced.""" return self.remaining_silence != 0 + @property + def has_donator(self) -> bool: + return self.donor_end > time.time() + @cached_property def bancho_priv(self) -> ClientPrivileges: """The player's privileges according to the client.""" @@ -354,7 +358,7 @@ def bancho_priv(self) -> ClientPrivileges: return ret @property - def restricted(self) -> bool: + def is_restricted(self) -> bool: """Return whether the player is restricted.""" return not self.priv & Privileges.UNRESTRICTED @@ -407,7 +411,7 @@ def logout(self) -> None: # enqueue logout to all users. app.state.sessions.players.remove(self) - if not self.restricted: + if not self.is_restricted: if app.state.services.datadog: app.state.services.datadog.decrement("bancho.online_players") @@ -937,7 +941,7 @@ async def relationships_from_sql(self, db_conn: databases.core.Connection) -> No self.friends.add(1) async def get_global_rank(self, mode: GameMode) -> int: - if self.restricted: + if self.is_restricted: return 0 rank = await app.state.services.redis.zrevrank( @@ -947,7 +951,7 @@ async def get_global_rank(self, mode: GameMode) -> int: return cast(int, rank) + 1 if rank is not None else 0 async def get_country_rank(self, mode: GameMode) -> int: - if self.restricted: + if self.is_restricted: return 0 country = self.geoloc["country"]["acronym"] @@ -962,7 +966,7 @@ async def update_rank(self, mode: GameMode) -> int: country = self.geoloc["country"]["acronym"] stats = self.stats[mode] - if not self.restricted: + if not self.is_restricted: # global rank await app.state.services.redis.zadd( f"bancho:leaderboard:{mode.value}",