mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-18 00:08:54 +00:00
osu: Allow returning a full file instead when requesting a table check/leaderboard
This commit is contained in:
parent
ed15406f51
commit
a36fa87964
4 changed files with 215 additions and 146 deletions
|
@ -6,7 +6,7 @@ use display::display_beatmapset;
|
||||||
use embeds::ScoreEmbedBuilder;
|
use embeds::ScoreEmbedBuilder;
|
||||||
use link_parser::EmbedType;
|
use link_parser::EmbedType;
|
||||||
use poise::{ChoiceParameter, CreateReply};
|
use poise::{ChoiceParameter, CreateReply};
|
||||||
use serenity::all::User;
|
use serenity::all::{CreateAttachment, User};
|
||||||
use server_rank::get_leaderboard_from_embed;
|
use server_rank::get_leaderboard_from_embed;
|
||||||
|
|
||||||
/// osu!-related command group.
|
/// osu!-related command group.
|
||||||
|
@ -581,6 +581,7 @@ async fn leaderboard<U: HasOsuEnv>(
|
||||||
"Here are the top scores of **{}** on {}",
|
"Here are the top scores of **{}** on {}",
|
||||||
guild.name, scoreboard_msg,
|
guild.name, scoreboard_msg,
|
||||||
);
|
);
|
||||||
|
let has_lazer_score = scores.iter().any(|v| v.score.mods.is_lazer);
|
||||||
|
|
||||||
match style {
|
match style {
|
||||||
ScoreListStyle::Table => {
|
ScoreListStyle::Table => {
|
||||||
|
@ -589,11 +590,30 @@ async fn leaderboard<U: HasOsuEnv>(
|
||||||
ctx.serenity_context(),
|
ctx.serenity_context(),
|
||||||
reply,
|
reply,
|
||||||
scores,
|
scores,
|
||||||
|
has_lazer_score,
|
||||||
show_diff,
|
show_diff,
|
||||||
sort.unwrap_or_default(),
|
sort.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
ScoreListStyle::File => {
|
||||||
|
ctx.send(
|
||||||
|
CreateReply::default()
|
||||||
|
.content(header)
|
||||||
|
.attachment(CreateAttachment::bytes(
|
||||||
|
server_rank::rankings_to_table(
|
||||||
|
&scores,
|
||||||
|
0,
|
||||||
|
scores.len(),
|
||||||
|
has_lazer_score,
|
||||||
|
show_diff,
|
||||||
|
order,
|
||||||
|
),
|
||||||
|
"rankings.txt",
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
ScoreListStyle::Grid => {
|
ScoreListStyle::Grid => {
|
||||||
let reply = ctx.reply(header).await?;
|
let reply = ctx.reply(header).await?;
|
||||||
style
|
style
|
||||||
|
|
|
@ -16,6 +16,8 @@ mod scores {
|
||||||
Table,
|
Table,
|
||||||
#[name = "List of Embeds"]
|
#[name = "List of Embeds"]
|
||||||
Grid,
|
Grid,
|
||||||
|
#[name = "Table File"]
|
||||||
|
File,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ScoreListStyle {
|
impl Default for ScoreListStyle {
|
||||||
|
@ -46,6 +48,7 @@ mod scores {
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
ScoreListStyle::Table => table::display_scores_table(scores, ctx, m).await,
|
ScoreListStyle::Table => table::display_scores_table(scores, ctx, m).await,
|
||||||
|
ScoreListStyle::File => table::display_scores_as_file(scores, ctx, m).await,
|
||||||
ScoreListStyle::Grid => grid::display_scores_grid(scores, ctx, guild_id, m).await,
|
ScoreListStyle::Grid => grid::display_scores_grid(scores, ctx, guild_id, m).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,7 +155,7 @@ mod scores {
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use pagination::paginate_with_first_message;
|
use pagination::paginate_with_first_message;
|
||||||
use serenity::all::CreateActionRow;
|
use serenity::all::{CreateActionRow, CreateAttachment};
|
||||||
|
|
||||||
use youmubot_prelude::table_format::Align::{Left, Right};
|
use youmubot_prelude::table_format::Align::{Left, Right};
|
||||||
use youmubot_prelude::table_format::{table_formatting, Align};
|
use youmubot_prelude::table_format::{table_formatting, Align};
|
||||||
|
@ -162,6 +165,29 @@ mod scores {
|
||||||
use crate::discord::{time_before_now, Beatmap, BeatmapInfo, OsuEnv};
|
use crate::discord::{time_before_now, Beatmap, BeatmapInfo, OsuEnv};
|
||||||
use crate::models::Score;
|
use crate::models::Score;
|
||||||
|
|
||||||
|
pub async fn display_scores_as_file(
|
||||||
|
scores: Vec<Score>,
|
||||||
|
ctx: &Context,
|
||||||
|
mut on: impl CanEdit,
|
||||||
|
) -> Result<()> {
|
||||||
|
if scores.is_empty() {
|
||||||
|
on.apply_edit(CreateReply::default().content("No plays found"))
|
||||||
|
.await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let p = Paginate {
|
||||||
|
env: ctx.data.read().await.get::<OsuEnv>().unwrap().clone(),
|
||||||
|
header: on.get_message().await?.content.clone(),
|
||||||
|
scores,
|
||||||
|
};
|
||||||
|
let content = p.to_table(0, p.scores.len()).await;
|
||||||
|
on.apply_edit(
|
||||||
|
CreateReply::default().attachment(CreateAttachment::bytes(content, "table.txt")),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn display_scores_table(
|
pub async fn display_scores_table(
|
||||||
scores: Vec<Score>,
|
scores: Vec<Score>,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
|
@ -197,30 +223,13 @@ mod scores {
|
||||||
fn total_pages(&self) -> usize {
|
fn total_pages(&self) -> usize {
|
||||||
(self.scores.len() + ITEMS_PER_PAGE - 1) / ITEMS_PER_PAGE
|
(self.scores.len() + ITEMS_PER_PAGE - 1) / ITEMS_PER_PAGE
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const ITEMS_PER_PAGE: usize = 5;
|
async fn to_table(&self, start: usize, end: usize) -> String {
|
||||||
|
let scores = &self.scores[start..end];
|
||||||
|
let meta_cache = &self.env.beatmaps;
|
||||||
|
let oppai = &self.env.oppai;
|
||||||
|
|
||||||
#[async_trait]
|
let beatmaps = scores
|
||||||
impl pagination::Paginate for Paginate {
|
|
||||||
async fn render(
|
|
||||||
&mut self,
|
|
||||||
page: u8,
|
|
||||||
btns: Vec<CreateActionRow>,
|
|
||||||
) -> Result<Option<CreateReply>> {
|
|
||||||
let env = &self.env;
|
|
||||||
|
|
||||||
let meta_cache = &env.beatmaps;
|
|
||||||
let oppai = &env.oppai;
|
|
||||||
let page = page as usize;
|
|
||||||
let start = page * ITEMS_PER_PAGE;
|
|
||||||
let end = self.scores.len().min(start + ITEMS_PER_PAGE);
|
|
||||||
if start >= end {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let plays = &self.scores[start..end];
|
|
||||||
let beatmaps = plays
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|play| async move {
|
.map(|play| async move {
|
||||||
let beatmap = meta_cache.get_beatmap(play.beatmap_id, play.mode).await?;
|
let beatmap = meta_cache.get_beatmap(play.beatmap_id, play.mode).await?;
|
||||||
|
@ -234,7 +243,7 @@ mod scores {
|
||||||
.map(|v| v.ok())
|
.map(|v| v.ok())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let pps = plays
|
let pps = scores
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| async move {
|
.map(|p| async move {
|
||||||
match p.pp.map(|pp| format!("{:.2}", pp)) {
|
match p.pp.map(|pp| format!("{:.2}", pp)) {
|
||||||
|
@ -257,7 +266,7 @@ mod scores {
|
||||||
|
|
||||||
let (beatmaps, pps) = future::join(beatmaps, pps).await;
|
let (beatmaps, pps) = future::join(beatmaps, pps).await;
|
||||||
|
|
||||||
let ranks = plays
|
let ranks = scores
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, p)| -> Cow<'static, str> {
|
.map(|(i, p)| -> Cow<'static, str> {
|
||||||
|
@ -288,7 +297,7 @@ mod scores {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, b)| {
|
.map(|(i, b)| {
|
||||||
let play = &plays[i];
|
let play = &scores[i];
|
||||||
b.map(|(beatmap, info)| {
|
b.map(|(beatmap, info)| {
|
||||||
format!(
|
format!(
|
||||||
"[{:.1}*] {} - {} [{}] ({})",
|
"[{:.1}*] {} - {} [{}] ({})",
|
||||||
|
@ -307,7 +316,7 @@ mod scores {
|
||||||
["#", "PP", "Acc", "Ranks", "Mods", "When", "Beatmap"];
|
["#", "PP", "Acc", "Ranks", "Mods", "When", "Beatmap"];
|
||||||
const SCORE_ALIGNS: [Align; 7] = [Right, Right, Right, Right, Right, Right, Left];
|
const SCORE_ALIGNS: [Align; 7] = [Right, Right, Right, Right, Right, Right, Left];
|
||||||
|
|
||||||
let score_arr = plays
|
let score_arr = scores
|
||||||
.iter()
|
.iter()
|
||||||
.zip(beatmaps.iter())
|
.zip(beatmaps.iter())
|
||||||
.zip(ranks.iter().zip(pps.iter()))
|
.zip(ranks.iter().zip(pps.iter()))
|
||||||
|
@ -325,9 +334,30 @@ mod scores {
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let score_table = table_formatting(&SCORE_HEADERS, &SCORE_ALIGNS, score_arr);
|
table_formatting(&SCORE_HEADERS, &SCORE_ALIGNS, score_arr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ITEMS_PER_PAGE: usize = 5;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl pagination::Paginate for Paginate {
|
||||||
|
async fn render(
|
||||||
|
&mut self,
|
||||||
|
page: u8,
|
||||||
|
btns: Vec<CreateActionRow>,
|
||||||
|
) -> Result<Option<CreateReply>> {
|
||||||
|
let page = page as usize;
|
||||||
|
let start = page * ITEMS_PER_PAGE;
|
||||||
|
let end = self.scores.len().min(start + ITEMS_PER_PAGE);
|
||||||
|
if start >= end {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let plays = &self.scores[start..end];
|
||||||
|
|
||||||
let has_oppai = plays.iter().any(|p| p.pp.is_none());
|
let has_oppai = plays.iter().any(|p| p.pp.is_none());
|
||||||
|
|
||||||
|
let score_table = self.to_table(start, end).await;
|
||||||
let mut content = serenity::utils::MessageBuilder::new();
|
let mut content = serenity::utils::MessageBuilder::new();
|
||||||
content
|
content
|
||||||
.push_line(&self.header)
|
.push_line(&self.header)
|
||||||
|
|
|
@ -413,7 +413,8 @@ pub fn handle_lb_button<'a>(
|
||||||
.content(format!("Here are the top scores on {}!", scoreboard_msg)),
|
.content(format!("Here are the top scores on {}!", scoreboard_msg)),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
display_rankings_table(ctx, reply, scores, show_diff, order).await?;
|
let has_lazer_score = scores.iter().any(|s| s.score.mods.is_lazer);
|
||||||
|
display_rankings_table(ctx, reply, scores, has_lazer_score, show_diff, order).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use std::{
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use pagination::paginate_with_first_message;
|
use pagination::paginate_with_first_message;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
all::{GuildId, Member, PartialGuild},
|
all::{CreateAttachment, CreateMessage, GuildId, Member, PartialGuild},
|
||||||
framework::standard::{macros::command, Args, CommandResult},
|
framework::standard::{macros::command, Args, CommandResult},
|
||||||
model::channel::Message,
|
model::channel::Message,
|
||||||
utils::MessageBuilder,
|
utils::MessageBuilder,
|
||||||
|
@ -401,26 +401,41 @@ pub async fn show_leaderboard(ctx: &Context, msg: &Message, mut args: Args) -> C
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let header = format!(
|
||||||
|
"Here are the top scores of **{}** on {}",
|
||||||
|
guild.name(&ctx).unwrap(),
|
||||||
|
scoreboard_msg,
|
||||||
|
);
|
||||||
|
let has_lazer_score = scores.iter().any(|v| v.score.mods.is_lazer);
|
||||||
|
|
||||||
match style {
|
match style {
|
||||||
ScoreListStyle::Table => {
|
ScoreListStyle::Table => {
|
||||||
let reply = msg
|
let reply = msg.reply(&ctx, header).await?;
|
||||||
.reply(
|
display_rankings_table(ctx, reply, scores, has_lazer_score, show_diff, order).await?;
|
||||||
|
}
|
||||||
|
ScoreListStyle::File => {
|
||||||
|
msg.channel_id
|
||||||
|
.send_message(
|
||||||
&ctx,
|
&ctx,
|
||||||
format!("⌛ Loading top scores on {}...", scoreboard_msg),
|
CreateMessage::new()
|
||||||
|
.content(header)
|
||||||
|
.reference_message(msg)
|
||||||
|
.add_file(CreateAttachment::bytes(
|
||||||
|
rankings_to_table(
|
||||||
|
&scores,
|
||||||
|
0,
|
||||||
|
scores.len(),
|
||||||
|
has_lazer_score,
|
||||||
|
show_diff,
|
||||||
|
order,
|
||||||
|
),
|
||||||
|
"rankings.txt",
|
||||||
|
)),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
display_rankings_table(ctx, reply, scores, show_diff, order).await?;
|
|
||||||
}
|
}
|
||||||
ScoreListStyle::Grid => {
|
ScoreListStyle::Grid => {
|
||||||
let reply = msg
|
let reply = msg.reply(&ctx, header).await?;
|
||||||
.reply(
|
|
||||||
&ctx,
|
|
||||||
format!(
|
|
||||||
"Here are the top scores on {} of this server!",
|
|
||||||
scoreboard_msg
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
style
|
style
|
||||||
.display_scores(
|
.display_scores(
|
||||||
scores.into_iter().map(|s| s.score).collect(),
|
scores.into_iter().map(|s| s.score).collect(),
|
||||||
|
@ -600,19 +615,107 @@ pub async fn get_leaderboard_from_embed(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn rankings_to_table(
|
||||||
|
scores: &[Ranking],
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
|
has_lazer_score: bool,
|
||||||
|
show_diff: bool,
|
||||||
|
order: OrderBy,
|
||||||
|
) -> String {
|
||||||
|
assert!(start < end);
|
||||||
|
let headers: [&'static str; 9] = [
|
||||||
|
"#",
|
||||||
|
match order {
|
||||||
|
OrderBy::PP => "pp",
|
||||||
|
OrderBy::Score => "Score",
|
||||||
|
},
|
||||||
|
if show_diff { "Map" } else { "Mods" },
|
||||||
|
"Rank",
|
||||||
|
"Acc",
|
||||||
|
"Combo",
|
||||||
|
"Miss",
|
||||||
|
"When",
|
||||||
|
"User",
|
||||||
|
];
|
||||||
|
let aligns: [Align; 9] = [
|
||||||
|
Right,
|
||||||
|
Right,
|
||||||
|
if show_diff { Left } else { Right },
|
||||||
|
Right,
|
||||||
|
Right,
|
||||||
|
Right,
|
||||||
|
Right,
|
||||||
|
Right,
|
||||||
|
Left,
|
||||||
|
];
|
||||||
|
|
||||||
|
let score_arr = scores
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(
|
||||||
|
|(
|
||||||
|
id,
|
||||||
|
Ranking {
|
||||||
|
pp,
|
||||||
|
beatmap,
|
||||||
|
official,
|
||||||
|
member,
|
||||||
|
score,
|
||||||
|
star,
|
||||||
|
},
|
||||||
|
)| {
|
||||||
|
[
|
||||||
|
format!("{}", 1 + id + start),
|
||||||
|
match order {
|
||||||
|
OrderBy::PP => {
|
||||||
|
format!("{:.2}{}", pp, if *official { "" } else { "[?]" })
|
||||||
|
}
|
||||||
|
OrderBy::Score => {
|
||||||
|
crate::discord::embeds::grouped_number(if has_lazer_score {
|
||||||
|
score.normalized_score as u64
|
||||||
|
} else {
|
||||||
|
score.score
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
if show_diff {
|
||||||
|
let trimmed_diff = if beatmap.difficulty_name.len() > 20 {
|
||||||
|
let mut s = beatmap.difficulty_name.clone();
|
||||||
|
s.truncate(17);
|
||||||
|
s + "..."
|
||||||
|
} else {
|
||||||
|
beatmap.difficulty_name.clone()
|
||||||
|
};
|
||||||
|
format!("[{:.2}*] {} {}", star, trimmed_diff, score.mods.to_string())
|
||||||
|
} else {
|
||||||
|
score.mods.to_string()
|
||||||
|
},
|
||||||
|
score.rank.to_string(),
|
||||||
|
format!("{:.2}%", score.accuracy(score.mode)),
|
||||||
|
format!("{}x", score.max_combo),
|
||||||
|
format!("{}", score.count_miss),
|
||||||
|
time_before_now(&score.date),
|
||||||
|
member.to_string(),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
table_formatting(&headers, &aligns, &score_arr)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn display_rankings_table(
|
pub async fn display_rankings_table(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
to: Message,
|
to: Message,
|
||||||
scores: Vec<Ranking>,
|
scores: Vec<Ranking>,
|
||||||
|
has_lazer_score: bool,
|
||||||
show_diff: bool,
|
show_diff: bool,
|
||||||
order: OrderBy,
|
order: OrderBy,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let has_lazer_score = scores.iter().any(|v| v.score.mods.is_lazer);
|
|
||||||
|
|
||||||
const ITEMS_PER_PAGE: usize = 5;
|
const ITEMS_PER_PAGE: usize = 5;
|
||||||
let total_len = scores.len();
|
let total_len = scores.len();
|
||||||
let total_pages = (total_len + ITEMS_PER_PAGE - 1) / ITEMS_PER_PAGE;
|
let total_pages = (total_len + ITEMS_PER_PAGE - 1) / ITEMS_PER_PAGE;
|
||||||
let header = Arc::new(to.content.clone());
|
let header = to.content.clone();
|
||||||
|
|
||||||
paginate_with_first_message(
|
paginate_with_first_message(
|
||||||
paginate_from_fn(move |page: u8, btns| {
|
paginate_from_fn(move |page: u8, btns| {
|
||||||
|
@ -622,106 +725,21 @@ pub async fn display_rankings_table(
|
||||||
return Box::pin(future::ready(Ok(None)));
|
return Box::pin(future::ready(Ok(None)));
|
||||||
}
|
}
|
||||||
let scores = scores[start..end].to_vec();
|
let scores = scores[start..end].to_vec();
|
||||||
let header = header.clone();
|
let score_table =
|
||||||
Box::pin(async move {
|
rankings_to_table(&scores, start, end, has_lazer_score, show_diff, order);
|
||||||
let headers: [&'static str; 9] = [
|
let content = MessageBuilder::new()
|
||||||
"#",
|
.push_line(&header)
|
||||||
match order {
|
.push_line(score_table)
|
||||||
OrderBy::PP => "pp",
|
.push_line(format!(
|
||||||
OrderBy::Score => "Score",
|
"Page **{}**/**{}**. Not seeing your scores? Run `osu check` to update.",
|
||||||
},
|
page + 1,
|
||||||
if show_diff { "Map" } else { "Mods" },
|
total_pages,
|
||||||
"Rank",
|
|
||||||
"Acc",
|
|
||||||
"Combo",
|
|
||||||
"Miss",
|
|
||||||
"When",
|
|
||||||
"User",
|
|
||||||
];
|
|
||||||
let aligns: [Align; 9] = [
|
|
||||||
Right,
|
|
||||||
Right,
|
|
||||||
if show_diff { Left } else { Right },
|
|
||||||
Right,
|
|
||||||
Right,
|
|
||||||
Right,
|
|
||||||
Right,
|
|
||||||
Right,
|
|
||||||
Left,
|
|
||||||
];
|
|
||||||
|
|
||||||
let score_arr = scores
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(
|
|
||||||
|(
|
|
||||||
id,
|
|
||||||
Ranking {
|
|
||||||
pp,
|
|
||||||
beatmap,
|
|
||||||
official,
|
|
||||||
member,
|
|
||||||
score,
|
|
||||||
star,
|
|
||||||
},
|
|
||||||
)| {
|
|
||||||
[
|
|
||||||
format!("{}", 1 + id + start),
|
|
||||||
match order {
|
|
||||||
OrderBy::PP => {
|
|
||||||
format!("{:.2}{}", pp, if *official { "" } else { "[?]" })
|
|
||||||
}
|
|
||||||
OrderBy::Score => {
|
|
||||||
crate::discord::embeds::grouped_number(if has_lazer_score {
|
|
||||||
score.normalized_score as u64
|
|
||||||
} else {
|
|
||||||
score.score
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
if show_diff {
|
|
||||||
let trimmed_diff = if beatmap.difficulty_name.len() > 20 {
|
|
||||||
let mut s = beatmap.difficulty_name.clone();
|
|
||||||
s.truncate(17);
|
|
||||||
s + "..."
|
|
||||||
} else {
|
|
||||||
beatmap.difficulty_name.clone()
|
|
||||||
};
|
|
||||||
format!(
|
|
||||||
"[{:.2}*] {} {}",
|
|
||||||
star,
|
|
||||||
trimmed_diff,
|
|
||||||
score.mods.to_string()
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
score.mods.to_string()
|
|
||||||
},
|
|
||||||
score.rank.to_string(),
|
|
||||||
format!("{:.2}%", score.accuracy(score.mode)),
|
|
||||||
format!("{}x", score.max_combo),
|
|
||||||
format!("{}", score.count_miss),
|
|
||||||
time_before_now(&score.date),
|
|
||||||
member.to_string(),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let score_table = table_formatting(&headers, &aligns, score_arr);
|
|
||||||
let content = MessageBuilder::new()
|
|
||||||
.push_line(header.as_ref())
|
|
||||||
.push_line(score_table)
|
|
||||||
.push_line(format!(
|
|
||||||
"Page **{}**/**{}**. Not seeing your scores? Run `osu check` to update.",
|
|
||||||
page + 1,
|
|
||||||
total_pages,
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Ok(Some(
|
|
||||||
CreateReply::default().content(content).components(btns),
|
|
||||||
))
|
))
|
||||||
})
|
.build();
|
||||||
|
|
||||||
|
Box::pin(future::ready(Ok(Some(
|
||||||
|
CreateReply::default().content(content).components(btns),
|
||||||
|
))))
|
||||||
})
|
})
|
||||||
.with_page_count(total_pages),
|
.with_page_count(total_pages),
|
||||||
ctx,
|
ctx,
|
||||||
|
|
Loading…
Add table
Reference in a new issue