mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-10 04:30:29 +00:00
Add check command
This commit is contained in:
parent
0db52c5c2b
commit
3f9db46032
4 changed files with 190 additions and 34 deletions
|
@ -1,14 +1,26 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use super::*;
|
||||
use cache::save_beatmap;
|
||||
use display::display_beatmapset;
|
||||
use embeds::ScoreEmbedBuilder;
|
||||
use link_parser::EmbedType;
|
||||
use poise::CreateReply;
|
||||
use poise::{ChoiceParameter, CreateReply};
|
||||
use serenity::all::User;
|
||||
|
||||
/// osu!-related command group.
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
subcommands("profile", "top", "recent", "pinned", "save", "forcesave", "beatmap")
|
||||
subcommands(
|
||||
"profile",
|
||||
"top",
|
||||
"recent",
|
||||
"pinned",
|
||||
"save",
|
||||
"forcesave",
|
||||
"beatmap",
|
||||
"check"
|
||||
)
|
||||
)]
|
||||
pub async fn osu<U: HasOsuEnv>(_ctx: CmdContext<'_, U>) -> Result<()> {
|
||||
Ok(())
|
||||
|
@ -308,20 +320,7 @@ async fn beatmap<U: HasOsuEnv>(
|
|||
|
||||
ctx.defer().await?;
|
||||
|
||||
let beatmap = parse_map_input(ctx.channel_id(), env, map, mode).await?;
|
||||
|
||||
// override into beatmapset if needed
|
||||
let beatmap = if beatmapset == Some(true) {
|
||||
match beatmap {
|
||||
EmbedType::Beatmap(beatmap, _, _) => {
|
||||
let beatmaps = env.beatmaps.get_beatmapset(beatmap.beatmapset_id).await?;
|
||||
EmbedType::Beatmapset(beatmaps)
|
||||
}
|
||||
bm @ EmbedType::Beatmapset(_) => bm,
|
||||
}
|
||||
} else {
|
||||
beatmap
|
||||
};
|
||||
let beatmap = parse_map_input(ctx.channel_id(), env, map, mode, beatmapset).await?;
|
||||
|
||||
// override mods and mode if needed
|
||||
match beatmap {
|
||||
|
@ -361,6 +360,8 @@ async fn beatmap<U: HasOsuEnv>(
|
|||
)]),
|
||||
)
|
||||
.await?;
|
||||
let bmode = beatmap.mode.with_override(mode);
|
||||
save_beatmap(env, ctx.channel_id(), &BeatmapWithMode(beatmap, bmode)).await?;
|
||||
}
|
||||
EmbedType::Beatmapset(vec) => {
|
||||
let b0 = &vec[0];
|
||||
|
@ -389,6 +390,138 @@ async fn beatmap<U: HasOsuEnv>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, ChoiceParameter, Default)]
|
||||
enum SortScoreBy {
|
||||
#[default]
|
||||
PP,
|
||||
Score,
|
||||
#[name = "Maximum Combo"]
|
||||
Combo,
|
||||
#[name = "Miss Count"]
|
||||
Miss,
|
||||
Accuracy,
|
||||
}
|
||||
|
||||
impl SortScoreBy {
|
||||
fn compare(self, a: &Score, b: &Score) -> Ordering {
|
||||
match self {
|
||||
SortScoreBy::PP => {
|
||||
b.pp.unwrap_or(0.0)
|
||||
.partial_cmp(&a.pp.unwrap_or(0.0))
|
||||
.unwrap()
|
||||
}
|
||||
SortScoreBy::Score => b.normalized_score.cmp(&a.normalized_score),
|
||||
SortScoreBy::Combo => b.max_combo.cmp(&a.max_combo),
|
||||
SortScoreBy::Miss => b.count_miss.cmp(&a.count_miss),
|
||||
SortScoreBy::Accuracy => b.server_accuracy.partial_cmp(&a.server_accuracy).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Display your or a player's scores for a certain beatmap/beatmapset.
|
||||
#[poise::command(slash_command)]
|
||||
async fn check<U: HasOsuEnv>(
|
||||
ctx: CmdContext<'_, U>,
|
||||
#[description = "A link or shortlink to the beatmap or beatmapset"] map: Option<String>,
|
||||
#[description = "osu! username"] username: Option<String>,
|
||||
#[description = "Discord username"] discord_name: Option<User>,
|
||||
#[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 = "Find all scores in the beatmapset instead"] beatmapset: Option<bool>,
|
||||
#[description = "Score listing style"] style: Option<ScoreListStyle>,
|
||||
) -> Result<()> {
|
||||
let env = ctx.data().osu_env();
|
||||
|
||||
let user = arg_from_username_or_discord(username, discord_name);
|
||||
let args = ListingArgs::from_params(
|
||||
env,
|
||||
None,
|
||||
style.unwrap_or(ScoreListStyle::Grid),
|
||||
mode,
|
||||
user,
|
||||
ctx.author().id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
ctx.defer().await?;
|
||||
|
||||
let embed = parse_map_input(ctx.channel_id(), env, map, mode, beatmapset).await?;
|
||||
let beatmaps = match embed {
|
||||
EmbedType::Beatmap(beatmap, _, _) => {
|
||||
let nmode = beatmap.mode.with_override(mode);
|
||||
vec![BeatmapWithMode(*beatmap, nmode)]
|
||||
}
|
||||
EmbedType::Beatmapset(vec) => match mode {
|
||||
None => {
|
||||
let default_mode = vec[0].mode;
|
||||
vec.into_iter()
|
||||
.filter(|b| b.mode == default_mode)
|
||||
.map(|b| BeatmapWithMode(b, default_mode))
|
||||
.collect()
|
||||
}
|
||||
Some(m) => vec
|
||||
.into_iter()
|
||||
.filter(|b| b.mode == Mode::Std || b.mode == m)
|
||||
.map(|b| BeatmapWithMode(b, m))
|
||||
.collect(),
|
||||
},
|
||||
};
|
||||
|
||||
let display = if beatmaps.len() == 1 {
|
||||
format!(
|
||||
"[{}](<{}>)",
|
||||
beatmaps[0].0.short_link(None, Mods::NOMOD),
|
||||
beatmaps[0].0.link()
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"[/s/{}](<{}>)",
|
||||
beatmaps[0].0.beatmapset_id,
|
||||
beatmaps[0].0.beatmapset_link()
|
||||
)
|
||||
};
|
||||
|
||||
let ordering = sort.unwrap_or_default();
|
||||
let mut scores = do_check(env, &beatmaps, mods, &args.user).await?;
|
||||
if scores.is_empty() {
|
||||
ctx.reply(format!(
|
||||
"No plays found for {} on {} with the required criteria.",
|
||||
args.user.mention(),
|
||||
display
|
||||
))
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
scores.sort_unstable_by(|a, b| ordering.compare(a, b));
|
||||
if reverse == Some(true) {
|
||||
scores.reverse();
|
||||
}
|
||||
|
||||
let msg = ctx
|
||||
.clone()
|
||||
.reply(format!(
|
||||
"Here are the plays by {} on {}!",
|
||||
args.user.mention(),
|
||||
display
|
||||
))
|
||||
.await?
|
||||
.into_message()
|
||||
.await?;
|
||||
args.style
|
||||
.display_scores(
|
||||
scores,
|
||||
beatmaps[0].1,
|
||||
ctx.serenity_context(),
|
||||
ctx.guild_id(),
|
||||
msg,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn arg_from_username_or_discord(
|
||||
username: Option<String>,
|
||||
discord_name: Option<User>,
|
||||
|
@ -405,8 +538,9 @@ async fn parse_map_input(
|
|||
env: &OsuEnv,
|
||||
input: Option<String>,
|
||||
mode: Option<Mode>,
|
||||
beatmapset: Option<bool>,
|
||||
) -> Result<EmbedType> {
|
||||
Ok(match input {
|
||||
let output = match input {
|
||||
None => {
|
||||
let Some((BeatmapWithMode(b, mode), bmmods)) =
|
||||
load_beatmap(env, channel_id, None as Option<&'_ Message>).await
|
||||
|
@ -452,5 +586,20 @@ async fn parse_map_input(
|
|||
};
|
||||
results.embed
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// override into beatmapset if needed
|
||||
let output = if beatmapset == Some(true) {
|
||||
match output {
|
||||
EmbedType::Beatmap(beatmap, _, _) => {
|
||||
let beatmaps = env.beatmaps.get_beatmapset(beatmap.beatmapset_id).await?;
|
||||
EmbedType::Beatmapset(beatmaps)
|
||||
}
|
||||
bm @ EmbedType::Beatmapset(_) => bm,
|
||||
}
|
||||
} else {
|
||||
output
|
||||
};
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
|
|
@ -194,7 +194,7 @@ mod scores {
|
|||
}
|
||||
}
|
||||
|
||||
const ITEMS_PER_PAGE: usize = 5;
|
||||
const ITEMS_PER_PAGE: usize = 10;
|
||||
|
||||
#[async_trait]
|
||||
impl pagination::Paginate for Paginate {
|
||||
|
|
|
@ -79,7 +79,7 @@ pub fn handle_check_button<'a>(
|
|||
};
|
||||
let header = UserHeader::from(user.clone());
|
||||
|
||||
let scores = super::do_check(&env, &bm, Mods::NOMOD, &header).await?;
|
||||
let scores = super::do_check(&env, &vec![bm.clone()], None, &header).await?;
|
||||
if scores.is_empty() {
|
||||
comp.create_followup(
|
||||
&ctx,
|
||||
|
|
|
@ -21,7 +21,7 @@ use db::{OsuLastBeatmap, OsuSavedUsers, OsuUser, OsuUserMode};
|
|||
use embeds::{beatmap_embed, score_embed, user_embed};
|
||||
pub use hook::{dot_osu_hook, hook, score_hook};
|
||||
use server_rank::{SERVER_RANK_COMMAND, SHOW_LEADERBOARD_COMMAND};
|
||||
use stream::FuturesOrdered;
|
||||
use stream::{FuturesOrdered, FuturesUnordered};
|
||||
use youmubot_prelude::announcer::AnnouncerHandler;
|
||||
use youmubot_prelude::*;
|
||||
|
||||
|
@ -925,18 +925,15 @@ pub async fn check(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul
|
|||
}
|
||||
};
|
||||
let mode = bm.1;
|
||||
let mods = args
|
||||
.find::<UnparsedMods>()
|
||||
.ok()
|
||||
.unwrap_or_default()
|
||||
.to_mods(mode)?;
|
||||
let umods = args.find::<UnparsedMods>().ok();
|
||||
let mods = umods.clone().unwrap_or_default().to_mods(mode)?;
|
||||
let style = args
|
||||
.single::<ScoreListStyle>()
|
||||
.unwrap_or(ScoreListStyle::Grid);
|
||||
let username_arg = args.single::<UsernameArg>().ok();
|
||||
let (_, user) = user_header_from_args(username_arg, &env, msg).await?;
|
||||
|
||||
let scores = do_check(&env, &bm, &mods, &user).await?;
|
||||
let scores = do_check(&env, &vec![bm.clone()], umods, &user).await?;
|
||||
|
||||
if scores.is_empty() {
|
||||
msg.reply(&ctx, "No scores found").await?;
|
||||
|
@ -961,19 +958,29 @@ pub async fn check(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul
|
|||
|
||||
pub(crate) async fn do_check(
|
||||
env: &OsuEnv,
|
||||
bm: &BeatmapWithMode,
|
||||
mods: &Mods,
|
||||
bm: &[BeatmapWithMode],
|
||||
mods: Option<UnparsedMods>,
|
||||
user: &UserHeader,
|
||||
) -> Result<Vec<Score>> {
|
||||
let BeatmapWithMode(b, m) = bm;
|
||||
|
||||
let osu_client = &env.client;
|
||||
|
||||
let mut scores = osu_client
|
||||
.scores(b.beatmap_id, |f| f.user(UserID::ID(user.id)).mode(*m))
|
||||
let mut scores = bm
|
||||
.iter()
|
||||
.map(|bm| {
|
||||
let BeatmapWithMode(b, m) = bm;
|
||||
let mods = mods.clone().and_then(|t| t.to_mods(*m).ok());
|
||||
osu_client
|
||||
.scores(b.beatmap_id, |f| f.user(UserID::ID(user.id)).mode(*m))
|
||||
.map_ok(move |mut v| {
|
||||
v.retain(|s| mods.as_ref().is_none_or(|m| m.contains(&s.mods)));
|
||||
v
|
||||
})
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|s| s.mods.contains(mods))
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
scores.sort_by(|a, b| {
|
||||
b.pp.unwrap_or(-1.0)
|
||||
|
|
Loading…
Add table
Reference in a new issue