def format_new_score(mode: api.GameMode, score: dict, beatmap: dict, rank: int, stream_url: str = None): """ Format any score. There should be a member name/mention in front of this string. """ acc = calculate_acc(mode, score) return ( "[*{artist} - {title} [{version}]*]({host}b/{beatmap_id})\n" "**{pp}pp {stars:.2f}\u2605, {rank} {scoreboard_rank}+{mods}**" "```diff\n" " acc 300s 100s 50s miss combo\n" "{sign} {acc:<8.2%}{count300:<7}{count100:<7}{count50:<7}{countmiss:<7}{maxcombo}{max_combo}```" "{live}").format( host=host, sign="!" if acc == 1 else ("+" if score["perfect"] == "1" else "-"), mods=Mods.format_mods(int(score["enabled_mods"])), acc=acc, artist=beatmap["artist"].replace("*", "\*").replace("_", "\_"), title=beatmap["title"].replace("*", "\*").replace("_", "\_"), version=beatmap["version"], stars=float(beatmap["difficultyrating"]), max_combo="/{}".format(beatmap["max_combo"]) if mode in (api.GameMode.Standard, api.GameMode.Catch) else "", scoreboard_rank="#{} ".format(rank) if rank else "", live="**Watch live @** <{}>\n".format(stream_url) if stream_url else "", **score)
def format_new_score(mode: api.GameMode, score: dict, beatmap: dict, rank: int, stream_url: str=None): """ Format any score. There should be a member name/mention in front of this string. """ acc = calculate_acc(mode, score) return ( "set a new best (`#{pos}/{limit} +{diff:.2f}pp`) on *{artist} - {title}* **[{version}] {stars:.2f}\u2605**\n" "**{pp}pp, {rank} {scoreboard_rank}+{mods}**" "```diff\n" " acc 300s 100s 50s miss combo\n" "{sign} {acc:<8.2%}{count300:<8}{count100:<8}{count50:<8}{countmiss:<8}{maxcombo}{max_combo}```" "**Profile**: <https://osu.ppy.sh/u/{user_id}>\n" "**Beatmap**: <https://osu.ppy.sh/b/{beatmap_id}>" "{live}" ).format( limit=score_request_limit, sign="!" if acc == 1 else ("+" if score["perfect"] == "1" else "-"), mods=Mods.format_mods(int(score["enabled_mods"])), acc=acc, artist=beatmap["artist"].replace("*", "\*").replace("_", "\_"), title=beatmap["title"].replace("*", "\*").replace("_", "\_"), version=beatmap["version"], stars=float(beatmap["difficultyrating"]), max_combo="/{}".format(beatmap["max_combo"]) if mode in (api.GameMode.Standard, api.GameMode.Catch) else "", scoreboard_rank="#{} ".format(rank) if rank else "", live="\n**Watch live @** <{}>".format(stream_url) if stream_url else "", **score )
async def format_new_score(mode: api.GameMode, score: dict, beatmap: dict, rank: int = None, member: discord.Member = None): """ Format any score. There should be a member name/mention in front of this string. """ acc = calculate_acc(mode, score) return ( "[{i}{artist} - {title} [{version}]{i}]({host}b/{beatmap_id})\n" "**{pp}pp {stars:.2f}\u2605, {rank} {scoreboard_rank}+{mods}**" "```diff\n" " acc 300s 100s 50s miss combo\n" "{sign} {acc:<8.2%}{count300:<7}{count100:<7}{count50:<7}{countmiss:<7}{maxcombo}{max_combo}```" "{live}").format( host=host, sign="!" if acc == 1 else ("+" if score["perfect"] == "1" else "-"), mods=Mods.format_mods(int(score["enabled_mods"])), acc=acc, artist=beatmap["artist"].replace("_", "\_"), title=beatmap["title"].replace("_", "\_"), i="*" if "*" not in beatmap["artist"] + beatmap["title"] else "", # Escaping asterisk doesn't work in italics version=beatmap["version"], stars=float(beatmap["difficultyrating"]), max_combo="/{}".format(beatmap["max_combo"]) if mode in (api.GameMode.Standard, api.GameMode.Catch) else "", scoreboard_rank="#{} ".format(rank) if rank else "", live=await format_stream(member, score, beatmap) if member else "", **score)
async def format_minimal_score(mode: api.GameMode, score: dict, beatmap: dict, rank: int, member: discord.Member): """ Format any osu! score with minimal content. There should be a member name/mention in front of this string. """ acc = calculate_acc(mode, score) return ( "[*{artist} - {title} [{version}]*]({host}b/{beatmap_id})\n" "**{pp}pp {stars:.2f}\u2605, {rank} {acc:.2%} {scoreboard_rank}+{mods}**" "{live}").format( host=host, mods=Mods.format_mods(int(score["enabled_mods"])), acc=acc, artist=beatmap["artist"].replace("*", "\*").replace("_", "\_"), title=beatmap["title"].replace("*", "\*").replace("_", "\_"), version=beatmap["version"], stars=float(beatmap["difficultyrating"]), scoreboard_rank="#{} ".format(rank) if rank else "", live=await format_stream(member, score, beatmap), **score)
def format_minimal_score(mode: api.GameMode, score: dict, beatmap: dict, rank: int, stream_url: str=None): """ Format any osu! score with minimal content. There should be a member name/mention in front of this string. """ acc = calculate_acc(mode, score) return ( "set a new best on *{artist} - {title}* **[{version}] {stars:.2f}\u2605**\n" "**{pp}pp, {rank} {acc:.2%} {scoreboard_rank}+{mods}**\n" "**Beatmap**: <https://osu.ppy.sh/b/{beatmap_id}>" "{live}" ).format( mods=Mods.format_mods(int(score["enabled_mods"])), acc=acc, artist=beatmap["artist"].replace("*", "\*").replace("_", "\_"), title=beatmap["title"].replace("*", "\*").replace("_", "\_"), version=beatmap["version"], stars=float(beatmap["difficultyrating"]), scoreboard_rank="#{} ".format(rank) if rank else "", live="\n**Watch live @** <{}>".format(stream_url) if stream_url else "", **score )
async def get_potential_pp(score, beatmap, member: discord.Member, score_pp: float, use_acc: bool = False): """ Returns the potential pp or None if it shouldn't display """ potential_pp = None # Find the potentially gained pp in standard when not FC if get_mode(member.id) is api.GameMode.Standard and get_update_mode(member.id) is not UpdateModes.PP \ and int(score["maxcombo"]) < int(beatmap["max_combo"]): options = ["+" + Mods.format_mods(int(score["enabled_mods"]))] if use_acc: options.append("{acc:.2%}".format(acc=calculate_acc( api.GameMode.Standard, score, exclude_misses=True))) else: options.append(score["count100"] + "x100") options.append(score["count50"] + "x50") try: pp_stats = await calculate_pp( "https://osu.ppy.sh/b/{}".format(score["beatmap_id"]), *options) potential_pp = pp_stats.pp except: pass # Drop this info whenever the potential pp gain is negative. # The osu! API does not provide info on sliderbreak count and missed sliderend count, which results # in faulty calculation (very often negative relatively). Therefore, I will conclude that the score # was actually an FC and has missed sliderends when the gain is negative. if potential_pp - score_pp <= 0: potential_pp = None return potential_pp
async def notify_pp(member_id: str, data: dict): """ Notify any differences in pp and post the scores + rank/pp gained. """ # Only update pp when there is actually a difference if "old" not in data: return # Get the difference in pp since the old data old, new = data["old"], data["new"] pp_diff = get_diff(old, new, "pp_raw") # If the difference is too small or nothing, move on if pp_threshold > pp_diff > -pp_threshold: return rank_diff = -int(get_diff(old, new, "pp_rank")) country_rank_diff = -int(get_diff(old, new, "pp_country_rank")) accuracy_diff = get_diff(old, new, "accuracy") # Percent points difference member = data["member"] mode = get_mode(member_id) update_mode = get_update_mode(member_id) m = "" potential_pp = None # Since the user got pp they probably have a new score in their own top 100 # If there is a score, there is also a beatmap if update_mode is UpdateModes.PP: score = None else: score = await get_new_score(member_id) # If a new score was found, format the score if score: beatmap_search = await api.get_beatmaps(b=int(score["beatmap_id"]), m=mode.value, a=1) beatmap = api.lookup_beatmap(beatmap_search) stream_url = getattr(member.game, "url", None) # There might not be any events scoreboard_rank = None if new["events"]: scoreboard_rank = api.rank_from_events(new["events"], score["beatmap_id"]) # Find the potentially gained pp in standard when not FC if mode is api.GameMode.Standard and update_mode is not UpdateModes.PP and int( score["maxcombo"]) < int(beatmap["max_combo"]): options = [ score["count100"] + "x100", score["count50"] + "x50", "+" + Mods.format_mods(int(score["enabled_mods"])) ] try: potential_pp = await calculate_pp( "https://osu.ppy.sh/b/{}".format(score["beatmap_id"]), *options) except: pass if update_mode is UpdateModes.Minimal: m += format_minimal_score(mode, score, beatmap, scoreboard_rank, stream_url) + "\n" else: m += format_new_score(mode, score, beatmap, scoreboard_rank, stream_url) # Always add the difference in pp along with the ranks m += format_user_diff(mode, pp_diff, rank_diff, country_rank_diff, accuracy_diff, old["country"], new) # Send the message to all servers for server in client.servers: member = server.get_member(member_id) channels = get_notify_channels(server, "score") if not member or not channels: continue primary_server = get_primary_server(member.id) is_primary = True if primary_server is None else ( True if primary_server == server.id else False) # Format the url link and the username user_url = get_user_url(member.id) name = "{member.mention} [`{ripple}{name}`]({url})".format( member=member, name=new["username"], url=user_url, ripple="ripple: " if new["ripple"] else "") embed = discord.Embed(color=member.color, url=user_url) embed.description = m # The top line of the format will differ depending on whether we found a score or not if score: embed.description = "**{0} set a new best `(#{pos}/{1} +{diff:.2f}pp)` on**\n".format( name, score_request_limit, **score) + m else: embed.description = name + "\n" + m # Add potential pp in the footer if potential_pp: embed.set_footer(text="Potential: {0:,}pp, {1:+.2f}pp".format( potential_pp, potential_pp - float(score["pp"]))) for i, channel in enumerate(channels): try: await client.send_message(channel, embed=embed) # In the primary server and if the user sets a score, send a mention and delete it # This will only mention in the first channel of the server if use_mentions_in_scores and score and i == 0 and is_primary: mention = await client.send_message( channel, member.mention) await client.delete_message(mention) except discord.Forbidden: pass