mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-16 07:18:54 +00:00
357 lines
11 KiB
Rust
357 lines
11 KiB
Rust
use super::BeatmapWithMode;
|
|
use crate::models::{Beatmap, Mode, Rank, Score, User};
|
|
use chrono::Utc;
|
|
use serenity::{builder::CreateEmbed, utils::MessageBuilder};
|
|
use youmubot_prelude::*;
|
|
|
|
fn format_mode(actual: Mode, original: Mode) -> String {
|
|
if actual == original {
|
|
format!("{}", actual)
|
|
} else {
|
|
format!("{} (converted)", actual)
|
|
}
|
|
}
|
|
|
|
pub fn beatmap_embed<'a>(b: &'_ Beatmap, m: Mode, c: &'a mut CreateEmbed) -> &'a mut CreateEmbed {
|
|
c.title(
|
|
MessageBuilder::new()
|
|
.push_bold_safe(&b.artist)
|
|
.push(" - ")
|
|
.push_bold_safe(&b.title)
|
|
.push(" [")
|
|
.push_bold_safe(&b.difficulty_name)
|
|
.push("]")
|
|
.build(),
|
|
)
|
|
.author(|a| {
|
|
a.name(&b.creator)
|
|
.url(format!("https://osu.ppy.sh/users/{}", b.creator_id))
|
|
.icon_url(format!("https://a.ppy.sh/{}", b.creator_id))
|
|
})
|
|
.url(b.link())
|
|
.thumbnail(format!("https://b.ppy.sh/thumb/{}l.jpg", b.beatmapset_id))
|
|
.image(b.cover_url())
|
|
.color(0xffb6c1)
|
|
.field(
|
|
"Star Difficulty",
|
|
format!("{:.2}⭐", b.difficulty.stars),
|
|
false,
|
|
)
|
|
.field(
|
|
"Length",
|
|
MessageBuilder::new()
|
|
.push_bold_safe(Duration(b.total_length))
|
|
.push(" (")
|
|
.push_bold_safe(Duration(b.drain_length))
|
|
.push(" drain)")
|
|
.build(),
|
|
false,
|
|
)
|
|
.field("Circle Size", format!("{:.1}", b.difficulty.cs), true)
|
|
.field("Approach Rate", format!("{:.1}", b.difficulty.ar), true)
|
|
.field(
|
|
"Overall Difficulty",
|
|
format!("{:.1}", b.difficulty.od),
|
|
true,
|
|
)
|
|
.field("HP Drain", format!("{:.1}", b.difficulty.hp), true)
|
|
.field("BPM", b.bpm.round(), true)
|
|
.fields(b.difficulty.max_combo.map(|v| ("Max combo", v, true)))
|
|
.field("Mode", format_mode(m, b.mode), true)
|
|
.fields(b.source.as_ref().map(|v| ("Source", v, true)))
|
|
.field(
|
|
"Tags",
|
|
b.tags
|
|
.iter()
|
|
.map(|v| MessageBuilder::new().push_mono_safe(v).build())
|
|
.take(10)
|
|
.chain(std::iter::once("...".to_owned()))
|
|
.collect::<Vec<_>>()
|
|
.join(" "),
|
|
false,
|
|
)
|
|
.description(
|
|
MessageBuilder::new()
|
|
.push({
|
|
let link = format!(
|
|
"https://osu.ppy.sh/beatmapsets/{}/download",
|
|
b.beatmapset_id
|
|
);
|
|
format!(
|
|
"Download: [[Link]]({}) [[No Video]]({}?noVideo=1)",
|
|
link, link
|
|
)
|
|
})
|
|
.push_line(format!(" [[Beatmapset]]({})", b.beatmapset_link()))
|
|
.push_line(&b.approval)
|
|
.push("Language: ")
|
|
.push_bold(&b.language)
|
|
.push(" | Genre: ")
|
|
.push_bold(&b.genre)
|
|
.build(),
|
|
)
|
|
}
|
|
|
|
const MAX_DIFFS: usize = 25 - 4;
|
|
|
|
pub fn beatmapset_embed<'a>(
|
|
bs: &'_ [Beatmap],
|
|
m: Option<Mode>,
|
|
c: &'a mut CreateEmbed,
|
|
) -> &'a mut CreateEmbed {
|
|
let too_many_diffs = bs.len() > MAX_DIFFS;
|
|
let b: &Beatmap = &bs[0];
|
|
c.title(
|
|
MessageBuilder::new()
|
|
.push_bold_safe(&b.artist)
|
|
.push(" - ")
|
|
.push_bold_safe(&b.title)
|
|
.build(),
|
|
)
|
|
.author(|a| {
|
|
a.name(&b.creator)
|
|
.url(format!("https://osu.ppy.sh/users/{}", b.creator_id))
|
|
.icon_url(format!("https://a.ppy.sh/{}", b.creator_id))
|
|
})
|
|
.url(format!(
|
|
"https://osu.ppy.sh/beatmapsets/{}",
|
|
b.beatmapset_id,
|
|
))
|
|
// .thumbnail(format!("https://b.ppy.sh/thumb/{}l.jpg", b.beatmapset_id))
|
|
.image(format!(
|
|
"https://assets.ppy.sh/beatmaps/{}/covers/cover.jpg",
|
|
b.beatmapset_id
|
|
))
|
|
.color(0xffb6c1)
|
|
.description(
|
|
MessageBuilder::new()
|
|
.push_line({
|
|
let link = format!(
|
|
"https://osu.ppy.sh/beatmapsets/{}/download",
|
|
b.beatmapset_id
|
|
);
|
|
format!(
|
|
"Download: [[Link]]({}) [[No Video]]({}?noVideo=1)",
|
|
link, link
|
|
)
|
|
})
|
|
.push_line(&b.approval)
|
|
.push("Language: ")
|
|
.push_bold(&b.language)
|
|
.push(" | Genre: ")
|
|
.push_bold(&b.genre)
|
|
.build(),
|
|
)
|
|
.field(
|
|
"Length",
|
|
MessageBuilder::new()
|
|
.push_bold_safe(Duration(b.total_length))
|
|
.build(),
|
|
true,
|
|
)
|
|
.field("BPM", b.bpm.round(), true)
|
|
.fields(b.source.as_ref().map(|v| ("Source", v, false)))
|
|
.field(
|
|
"Tags",
|
|
b.tags
|
|
.iter()
|
|
.map(|v| MessageBuilder::new().push_mono_safe(v).build())
|
|
.take(10)
|
|
.chain(std::iter::once("...".to_owned()))
|
|
.collect::<Vec<_>>()
|
|
.join(" "),
|
|
false,
|
|
)
|
|
.footer(|f| {
|
|
if too_many_diffs {
|
|
f.text(format!(
|
|
"This map has {} diffs, we are showing the last {}.",
|
|
bs.len(),
|
|
MAX_DIFFS
|
|
))
|
|
} else {
|
|
f
|
|
}
|
|
})
|
|
.fields(bs.iter().rev().take(MAX_DIFFS).rev().map(|b: &Beatmap| {
|
|
(
|
|
format!("[{}]", b.difficulty_name),
|
|
MessageBuilder::new()
|
|
.push(format!("[[Link]]({})", b.link()))
|
|
.push(", ")
|
|
.push_bold(format!("{:.2}⭐", b.difficulty.stars))
|
|
.push(", ")
|
|
.push_bold_line(format_mode(m.unwrap_or(b.mode), b.mode))
|
|
.push("CS")
|
|
.push_bold(format!("{:.1}", b.difficulty.cs))
|
|
.push(", AR")
|
|
.push_bold(format!("{:.1}", b.difficulty.ar))
|
|
.push(", OD")
|
|
.push_bold(format!("{:.1}", b.difficulty.od))
|
|
.push(", HP")
|
|
.push_bold(format!("{:.1}", b.difficulty.hp))
|
|
.push(", ⌛ ")
|
|
.push_bold(format!("{}", Duration(b.drain_length)))
|
|
.build(),
|
|
false,
|
|
)
|
|
}))
|
|
}
|
|
|
|
pub(crate) fn score_embed<'a>(
|
|
s: &Score,
|
|
bm: &BeatmapWithMode,
|
|
u: &User,
|
|
top_record: Option<u8>,
|
|
m: &'a mut CreateEmbed,
|
|
) -> &'a mut CreateEmbed {
|
|
let mode = bm.mode();
|
|
let b = &bm.0;
|
|
let accuracy = s.accuracy(mode);
|
|
let score_line = match &s.rank {
|
|
Rank::SS | Rank::SSH => format!("SS"),
|
|
_ if s.perfect => format!("{:.2}% FC", accuracy),
|
|
Rank::F => format!("{:.2}% {} combo [FAILED]", accuracy, s.max_combo),
|
|
v => format!(
|
|
"{:.2}% {}x {} miss {} rank",
|
|
accuracy, s.max_combo, s.count_miss, v
|
|
),
|
|
};
|
|
let score_line =
|
|
s.pp.map(|pp| format!("{} | {:.2}pp", &score_line, pp))
|
|
.unwrap_or(score_line);
|
|
let top_record = top_record
|
|
.map(|v| format!("| #{} top record!", v))
|
|
.unwrap_or("".to_owned());
|
|
m.author(|f| f.name(&u.username).url(u.link()).icon_url(u.avatar_url()))
|
|
.color(0xffb6c1)
|
|
.title(format!(
|
|
"{} | {} - {} [{}] {} ({:.2}\\*) by {} | {} {}",
|
|
u.username,
|
|
b.artist,
|
|
b.title,
|
|
b.difficulty_name,
|
|
s.mods,
|
|
b.difficulty.stars,
|
|
b.creator,
|
|
score_line,
|
|
top_record
|
|
))
|
|
.description(format!("[[Beatmap]]({})", b.link()))
|
|
.image(b.cover_url())
|
|
.field(
|
|
"Beatmap",
|
|
format!("{} - {} [{}]", b.artist, b.title, b.difficulty_name),
|
|
false,
|
|
)
|
|
.field("Rank", &score_line, false)
|
|
.fields(s.pp.map(|pp| ("pp gained", format!("{:.2}pp", pp), true)))
|
|
.field("Creator", &b.creator, true)
|
|
.field("Mode", mode.to_string(), true)
|
|
.field(
|
|
"Map stats",
|
|
MessageBuilder::new()
|
|
.push(format!("[[Link]]({})", b.link()))
|
|
.push(", ")
|
|
.push_bold(format!("{:.2}⭐", b.difficulty.stars))
|
|
.push(", ")
|
|
.push_bold_line(
|
|
b.mode.to_string()
|
|
+ if bm.is_converted() {
|
|
" (Converted)"
|
|
} else {
|
|
""
|
|
},
|
|
)
|
|
.push("CS")
|
|
.push_bold(format!("{:.1}", b.difficulty.cs))
|
|
.push(", AR")
|
|
.push_bold(format!("{:.1}", b.difficulty.ar))
|
|
.push(", OD")
|
|
.push_bold(format!("{:.1}", b.difficulty.od))
|
|
.push(", HP")
|
|
.push_bold(format!("{:.1}", b.difficulty.hp))
|
|
.push(", ⌛ ")
|
|
.push_bold(format!("{}", Duration(b.drain_length)))
|
|
.build(),
|
|
false,
|
|
)
|
|
.field("Played on", s.date.format("%F %T"), false)
|
|
}
|
|
|
|
pub(crate) fn user_embed<'a>(
|
|
u: User,
|
|
best: Option<(Score, BeatmapWithMode)>,
|
|
m: &'a mut CreateEmbed,
|
|
) -> &'a mut CreateEmbed {
|
|
m.title(u.username)
|
|
.url(format!("https://osu.ppy.sh/users/{}", u.id))
|
|
.color(0xffb6c1)
|
|
.thumbnail(format!("https://a.ppy.sh/{}", u.id))
|
|
.description(format!("Member since **{}**", u.joined.format("%F %T")))
|
|
.field(
|
|
"Performance Points",
|
|
u.pp.map(|v| format!("{:.2}pp", v))
|
|
.unwrap_or("Inactive".to_owned()),
|
|
false,
|
|
)
|
|
.field("World Rank", format!("#{}", u.rank), true)
|
|
.field(
|
|
"Country Rank",
|
|
format!(":flag_{}: #{}", u.country.to_lowercase(), u.country_rank),
|
|
true,
|
|
)
|
|
.field("Accuracy", format!("{:.2}%", u.accuracy), true)
|
|
.field(
|
|
"Play count",
|
|
format!("{} (play time: {})", u.play_count, Duration(u.played_time)),
|
|
false,
|
|
)
|
|
.field(
|
|
"Ranks",
|
|
format!(
|
|
"{} SSH | {} SS | {} SH | {} S | {} A",
|
|
u.count_ssh, u.count_ss, u.count_sh, u.count_s, u.count_a
|
|
),
|
|
false,
|
|
)
|
|
.field(
|
|
"Level",
|
|
format!(
|
|
"Level **{:.0}**: {} total score, {} ranked score",
|
|
u.level, u.total_score, u.ranked_score
|
|
),
|
|
false,
|
|
)
|
|
.fields(best.map(|(v, map)| {
|
|
let map = map.0;
|
|
(
|
|
"Best Record",
|
|
MessageBuilder::new()
|
|
.push_bold(format!(
|
|
"{:.2}pp",
|
|
v.pp.unwrap() /*Top record should have pp*/
|
|
))
|
|
.push(" - ")
|
|
.push_line(format!(
|
|
"{:.1} ago",
|
|
Duration(
|
|
(Utc::now() - v.date)
|
|
.to_std()
|
|
.unwrap_or(std::time::Duration::from_secs(1))
|
|
)
|
|
))
|
|
.push("on ")
|
|
.push(format!(
|
|
"[{} - {}]({})",
|
|
MessageBuilder::new().push_bold_safe(&map.artist).build(),
|
|
MessageBuilder::new().push_bold_safe(&map.title).build(),
|
|
map.link()
|
|
))
|
|
.push(format!(" [{}]", map.difficulty_name))
|
|
.push(format!(" ({:.1}⭐)", map.difficulty.stars))
|
|
.build(),
|
|
false,
|
|
)
|
|
}))
|
|
}
|