Add leaderboard command

This commit is contained in:
Natsu Kagami 2024-12-31 09:01:03 +01:00 committed by Natsu Kagami
parent 5d9fda682e
commit ba74465ca7
3 changed files with 118 additions and 13 deletions

View file

@ -20,7 +20,8 @@ use serenity::all::User;
"forcesave",
"beatmap",
"check",
"ranks"
"ranks",
"leaderboard"
)
)]
pub async fn osu<U: HasOsuEnv>(_ctx: CmdContext<'_, U>) -> Result<()> {
@ -429,7 +430,7 @@ async fn check<U: HasOsuEnv>(
#[description = "Sort scores by"] sort: Option<SortScoreBy>,
#[description = "Reverse the sorting order"] reverse: Option<bool>,
#[description = "Filter the mods on the scores"] mods: Option<UnparsedMods>,
#[description = "Filter the mode of the scores"] mode: Option<Mode>,
#[description = "Filter the gamemode of the scores"] mode: Option<Mode>,
#[description = "Find all scores in the beatmapset instead"] beatmapset: Option<bool>,
#[description = "Score listing style"] style: Option<ScoreListStyle>,
) -> Result<()> {
@ -510,7 +511,14 @@ async fn check<U: HasOsuEnv>(
.await?
.into_message()
.await?;
args.style
let style = style.unwrap_or(if scores.len() <= 5 {
ScoreListStyle::Grid
} else {
ScoreListStyle::Table
});
style
.display_scores(
scores,
beatmaps[0].1,
@ -550,6 +558,90 @@ async fn ranks<U: HasOsuEnv>(
Ok(())
}
/// Display the leaderboard on a single map of members in the server.
#[poise::command(slash_command, guild_only)]
async fn leaderboard<U: HasOsuEnv>(
ctx: CmdContext<'_, U>,
#[description = "The link or shortlink of the map"] map: Option<String>,
#[description = "Sort scores by"] sort: Option<server_rank::OrderBy>,
#[description = "Reverse the ordering"] reverse: Option<bool>,
#[description = "Include unranked scores"] unranked: Option<bool>,
#[description = "Filter the gamemode of the scores"] mode: Option<Mode>,
#[description = "Score listing style"] style: Option<ScoreListStyle>,
) -> Result<()> {
let env = ctx.data().osu_env();
let guild = ctx.partial_guild().await.unwrap();
let style = style.unwrap_or_default();
let bm = match parse_map_input(ctx.channel_id(), env, map, mode, None).await? {
EmbedType::Beatmap(beatmap, _, _) => {
let nmode = beatmap.mode.with_override(mode);
BeatmapWithMode(*beatmap, nmode)
}
EmbedType::Beatmapset(_) => return Err(Error::msg("invalid map link")),
};
ctx.defer().await?;
let mut scores = server_rank::get_leaderboard(
ctx.serenity_context(),
env,
&bm,
unranked.unwrap_or(false),
sort.unwrap_or(server_rank::OrderBy::PP),
guild.id,
)
.await?;
if reverse == Some(true) {
scores.reverse();
}
let beatmap = &bm.0;
if scores.is_empty() {
ctx.reply(format!(
"No scores have been recorded in **{}** on [`{}`]({}).",
guild.name,
beatmap.short_link(mode, Mods::NOMOD),
beatmap.link()
))
.await?;
return Ok(());
}
let header = format!(
"Here are the top scores of **{}** on {}",
guild.name,
beatmap.mention(mode, Mods::NOMOD),
);
match style {
ScoreListStyle::Table => {
let reply = ctx.reply(header).await?.into_message().await?;
server_rank::display_rankings_table(
ctx.serenity_context(),
reply,
scores,
&bm,
sort.unwrap_or_default(),
)
.await?;
}
ScoreListStyle::Grid => {
let reply = ctx.reply(header).await?.into_message().await?;
style
.display_scores(
scores.into_iter().map(|s| s.score).collect(),
bm.1,
ctx.serenity_context(),
Some(guild.id),
reply,
)
.await?;
}
}
Ok(())
}
fn arg_from_username_or_discord(
username: Option<String>,
discord_name: Option<User>,

View file

@ -328,7 +328,7 @@ where
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, poise::ChoiceParameter)]
pub enum OrderBy {
PP,
Score,
@ -376,14 +376,12 @@ pub async fn show_leaderboard(ctx: &Context, msg: &Message, mut args: Args) -> C
let style = args.single::<ScoreListStyle>().unwrap_or_default();
let guild = msg.guild_id.expect("Guild-only command");
let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone();
let bm = match super::load_beatmap(&env, msg.channel_id, msg.referenced_message.as_ref()).await
{
Some((bm, _)) => bm,
None => {
msg.reply(&ctx, "No beatmap queried on this channel.")
.await?;
return Ok(());
}
let Some((bm, _)) =
super::load_beatmap(&env, msg.channel_id, msg.referenced_message.as_ref()).await
else {
msg.reply(&ctx, "No beatmap queried on this channel.")
.await?;
return Ok(());
};
let scores = {

View file

@ -414,9 +414,16 @@ impl Beatmap {
/// Gets a link pointing to the beatmap, in the new format.
pub fn link(&self) -> String {
self.mode_link(None)
}
/// Gets a link pointing to the beatmap, in the new format, with overridable mode.
pub fn mode_link(&self, mode: Option<Mode>) -> String {
format!(
"https://osu.ppy.sh/beatmapsets/{}#{}/{}",
self.beatmapset_id, NEW_MODE_NAMES[self.mode as usize], self.beatmap_id
self.beatmapset_id,
NEW_MODE_NAMES[self.mode.with_override(mode) as usize],
self.beatmap_id
)
}
@ -443,6 +450,14 @@ impl Beatmap {
)
}
pub fn mention(&self, override_mode: Option<Mode>, mods: &Mods) -> String {
format!(
"[`{}`]({})",
self.short_link(override_mode, mods),
self.mode_link(override_mode),
)
}
/// Link to the cover image of the beatmap.
pub fn cover_url(&self) -> String {
format!(