top and recent defaults to showing a table

This commit is contained in:
Natsu Kagami 2020-02-10 19:19:20 -05:00
parent 15232b1598
commit 7070296964
Signed by: nki
GPG key ID: 73376E117CD20735

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
models::{Beatmap, Mode, User}, models::{Beatmap, Mode, Score, User},
request::{BeatmapRequestKind, UserID}, request::{BeatmapRequestKind, UserID},
Client as OsuHttpClient, Client as OsuHttpClient,
}; };
@ -8,7 +8,7 @@ use serenity::{
macros::{command, group}, macros::{command, group},
Args, CommandError as Error, CommandResult, Args, CommandError as Error, CommandResult,
}, },
model::{channel::Message, id::UserId}, model::channel::Message,
utils::MessageBuilder, utils::MessageBuilder,
}; };
use std::str::FromStr; use std::str::FromStr;
@ -198,27 +198,91 @@ fn to_user_id_query(
.map(|u| UserID::ID(u.id)) .map(|u| UserID::ID(u.id))
.ok_or(Error::from("No saved account found")) .ok_or(Error::from("No saved account found"))
} }
struct Nth(u8);
enum Nth {
All,
Nth(u8),
}
impl FromStr for Nth { impl FromStr for Nth {
type Err = Error; type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
if !s.starts_with("#") { if s == "--all" || s == "-a" || s == "##" {
Ok(Nth::All)
} else if !s.starts_with("#") {
Err(Error::from("Not an order")) Err(Error::from("Not an order"))
} else { } else {
let v = s.split_at("#".len()).1.parse()?; let v = s.split_at("#".len()).1.parse()?;
Ok(Nth(v)) Ok(Nth::Nth(v))
} }
} }
} }
fn list_plays(plays: &[Score], mode: Mode, ctx: Context, m: &Message) -> CommandResult {
let watcher = ctx.data.get_cloned::<ReactionWatcher>();
let osu = ctx.data.get_cloned::<OsuClient>();
if plays.is_empty() {
m.reply(&ctx, "No plays found")?;
return Ok(());
}
let mut beatmaps: Vec<Option<String>> = vec![None; plays.len()];
const ITEMS_PER_PAGE: usize = 10;
let total_pages = (plays.len() + ITEMS_PER_PAGE - 1) / ITEMS_PER_PAGE;
watcher.paginate_fn(ctx, m.channel_id, |page, e| {
let page = page as usize;
let start = page * ITEMS_PER_PAGE;
let end = plays.len().min(start + ITEMS_PER_PAGE);
if start >= end {
return (e, Err(Error::from("No more pages")));
}
let plays = &plays[start..end];
let beatmaps = {
let b = &mut beatmaps[start..end];
b.iter_mut().enumerate().map(
|(i, v)| v.get_or_insert_with(
|| osu.beatmaps(BeatmapRequestKind::Beatmap(plays[i].beatmap_id), |f| f)
.ok()
.and_then(|v| v.into_iter().next())
.map(|b| format!("{} - {} [{}] (#{})", b.artist, b.title, b.difficulty_name, b.beatmap_id))
.unwrap_or("FETCH FAILED".to_owned()))).collect::<Vec<_>>()
};
let /*mods width*/ mw = plays.iter().map(|v| v.mods.to_string().len()).max().unwrap().max(4);
let /*beatmap names*/ bw = beatmaps.iter().map(|v| v.len()).max().unwrap().max(7);
let mut m = MessageBuilder::new();
m.push_line("```");
// Table header
m.push_line(format!(" # | pp | accuracy | rank | {:mw$} | {:bw$}", "mods", "beatmap", mw = mw, bw = bw));
m.push_line(format!("---------------------------------{:-<mw$}---{:-<bw$}", "", "", mw = mw, bw = bw));
// Each row
for (id, (play, beatmap)) in plays.iter().zip(beatmaps.iter()).enumerate() {
m.push_line_safe(
format!(
"{:>3} | {:>6} | {:>8} | {:^4} | {:mw$} | {:bw$}",
id + start + 1,
play.pp.map(|v| format!("{:.2}", v)).unwrap_or("-".to_owned()),
format!("{:.2}%", play.accuracy(mode)),
play.rank.to_string(), play.mods.to_string(), beatmap, mw = mw, bw = bw));
}
// End
m.push_line("```").push_line(format!("Page **{}/{}**", page + 1, total_pages));
(e.content(m.build()), Ok(()))
}, std::time::Duration::from_secs(60))
}
#[command] #[command]
#[description = "Gets an user's recent play"] #[description = "Gets an user's recent play"]
#[usage = "#[the nth recent play = 1] / [mode (std, taiko, mania, catch) = std] / [username / user id = your saved id]"] #[usage = "#[the nth recent play = --all] / [mode (std, taiko, mania, catch) = std] / [username / user id = your saved id]"]
#[example = "#1 / taiko / natsukagami"] #[example = "#1 / taiko / natsukagami"]
#[max_args(3)] #[max_args(3)]
pub fn recent(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { pub fn recent(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
let nth = args.single::<Nth>().unwrap_or(Nth(1)).0.min(50).max(1); let nth = args.single::<Nth>().unwrap_or(Nth::All);
let mode = args.single::<ModeArg>().unwrap_or(ModeArg(Mode::Std)).0; let mode = args.single::<ModeArg>().unwrap_or(ModeArg(Mode::Std)).0;
let user = to_user_id_query(args.single::<UsernameArg>().ok(), &*ctx.data.read(), msg)?; let user = to_user_id_query(args.single::<UsernameArg>().ok(), &*ctx.data.read(), msg)?;
@ -226,6 +290,8 @@ pub fn recent(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult
let user = osu let user = osu
.user(user, |f| f.mode(mode))? .user(user, |f| f.mode(mode))?
.ok_or(Error::from("User not found"))?; .ok_or(Error::from("User not found"))?;
match nth {
Nth::Nth(nth) => {
let recent_play = osu let recent_play = osu
.user_recent(UserID::ID(user.id), |f| f.mode(mode).limit(nth))? .user_recent(UserID::ID(user.id), |f| f.mode(mode).limit(nth))?
.into_iter() .into_iter()
@ -250,7 +316,12 @@ pub fn recent(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult
// Save the beatmap... // Save the beatmap...
cache::save_beatmap(&*ctx.data.read(), msg.channel_id, &beatmap)?; cache::save_beatmap(&*ctx.data.read(), msg.channel_id, &beatmap)?;
}
Nth::All => {
let plays = osu.user_recent(UserID::ID(user.id), |f| f.mode(mode).limit(50))?;
list_plays(&plays, mode, ctx.clone(), msg)?;
}
}
Ok(()) Ok(())
} }
@ -318,11 +389,11 @@ pub fn check(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult
#[command] #[command]
#[description = "Get the n-th top record of an user."] #[description = "Get the n-th top record of an user."]
#[usage = "#[n-th = 1] / [mode (std, taiko, catch, mania) = std / [username or user_id = your saved user id]"] #[usage = "#[n-th = --all] / [mode (std, taiko, catch, mania) = std / [username or user_id = your saved user id]"]
#[example = "#2 / taiko / natsukagami"] #[example = "#2 / taiko / natsukagami"]
#[max_args(3)] #[max_args(3)]
pub fn top(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { pub fn top(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
let nth = args.single::<Nth>().unwrap_or(Nth(1)).0; let nth = args.single::<Nth>().unwrap_or(Nth::All);
let mode = args let mode = args
.single::<ModeArg>() .single::<ModeArg>()
.map(|ModeArg(t)| t) .map(|ModeArg(t)| t)
@ -334,6 +405,9 @@ pub fn top(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
let user = osu let user = osu
.user(user, |f| f.mode(mode))? .user(user, |f| f.mode(mode))?
.ok_or(Error::from("User not found"))?; .ok_or(Error::from("User not found"))?;
match nth {
Nth::Nth(nth) => {
let top_play = osu.user_best(UserID::ID(user.id), |f| f.mode(mode).limit(nth))?; let top_play = osu.user_best(UserID::ID(user.id), |f| f.mode(mode).limit(nth))?;
let rank = top_play.len() as u8; let rank = top_play.len() as u8;
@ -361,7 +435,12 @@ pub fn top(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
// Save the beatmap... // Save the beatmap...
cache::save_beatmap(&*ctx.data.read(), msg.channel_id, &beatmap)?; cache::save_beatmap(&*ctx.data.read(), msg.channel_id, &beatmap)?;
}
Nth::All => {
let plays = osu.user_best(UserID::ID(user.id), |f| f.mode(mode).limit(100))?;
list_plays(&plays, mode, ctx.clone(), msg)?;
}
}
Ok(()) Ok(())
} }