Add short link references in embeds

This commit is contained in:
Natsu Kagami 2020-06-14 21:40:36 -04:00
parent 5c270db9cb
commit cafa65581c
Signed by: nki
GPG key ID: 73376E117CD20735
4 changed files with 140 additions and 27 deletions

View file

@ -109,7 +109,11 @@ pub fn beatmap_embed<'a>(
)
})
.push_line(format!(" [[Beatmapset]]({})", b.beatmapset_link()))
.push_line(&b.approval)
.push_line(format!(
"Short link: `{}`",
b.short_link(Some(m), Some(mods))
))
.push_bold_line(&b.approval)
.push("Language: ")
.push_bold(&b.language)
.push(" | Genre: ")
@ -209,7 +213,11 @@ pub fn beatmapset_embed<'a>(
(
format!("[{}]", b.difficulty_name),
MessageBuilder::new()
.push(format!("[[Link]]({})", b.link()))
.push(format!(
"[[Link]]({}) (`{}`)",
b.link(),
b.short_link(m, None)
))
.push(", ")
.push_bold(format!("{:.2}", b.difficulty.stars))
.push(", ")
@ -311,7 +319,11 @@ pub(crate) fn score_embed<'a>(
.field(
"Map stats",
MessageBuilder::new()
.push(format!("[[Link]]({})", b.link()))
.push(format!(
"[[Link]]({}) (`{}`)",
b.link(),
b.short_link(Some(mode), Some(s.mods))
))
.push(", ")
.push_bold(format!("{:.2}", stars))
.push(", ")
@ -346,7 +358,7 @@ pub(crate) fn score_embed<'a>(
pub(crate) fn user_embed<'a>(
u: User,
best: Option<(Score, BeatmapWithMode)>,
best: Option<(Score, BeatmapWithMode, Option<BeatmapInfo>)>,
m: &'a mut CreateEmbed,
) -> &'a mut CreateEmbed {
m.title(u.username)
@ -388,8 +400,8 @@ pub(crate) fn user_embed<'a>(
),
false,
)
.fields(best.map(|(v, map)| {
let map = map.0;
.fields(best.map(|(v, map, info)| {
let BeatmapWithMode(map, mode) = map;
(
"Best Record",
MessageBuilder::new()
@ -413,8 +425,12 @@ pub(crate) fn user_embed<'a>(
MessageBuilder::new().push_bold_safe(&map.title).build(),
map.link()
))
.push(format!(" [{}]", map.difficulty_name))
.push(format!(" ({:.1}⭐)", map.difficulty.stars))
.push_line(format!(" [{}]", map.difficulty_name))
.push(format!(
"{:.1}⭐ | `{}`",
info.map(|i| i.stars as f64).unwrap_or(map.difficulty.stars),
map.short_link(Some(mode), Some(v.mods))
))
.build(),
false,
)

View file

@ -1,5 +1,6 @@
use super::OsuClient;
use crate::{
discord::beatmap_cache::BeatmapMetaCache,
discord::oppai_cache::{BeatmapCache, BeatmapInfo},
models::{Beatmap, Mode, Mods},
request::BeatmapRequestKind,
@ -24,6 +25,9 @@ lazy_static! {
static ref NEW_LINK_REGEX: Regex = Regex::new(
r"https?://osu\.ppy\.sh/beatmapsets/(?P<set_id>\d+)/?(?:\#(?P<mode>osu|taiko|fruits|mania)(?:/(?P<beatmap_id>\d+)|/?))?(?:\+(?P<mods>[A-Z]+))?"
).unwrap();
static ref SHORT_LINK_REGEX: Regex = Regex::new(
r"/b/(?P<id>\d+)(?:/(?P<mode>osu|taiko|fruits|mania))?(?:\+(?P<mods>[A-Z]+))?"
).unwrap();
}
pub fn hook(ctx: &mut Context, msg: &Message) -> () {
@ -33,8 +37,13 @@ pub fn hook(ctx: &mut Context, msg: &Message) -> () {
let mut v = move || -> CommandResult {
let old_links = handle_old_links(ctx, &msg.content)?;
let new_links = handle_new_links(ctx, &msg.content)?;
let short_links = handle_short_links(ctx, &msg, &msg.content)?;
let mut last_beatmap = None;
for l in old_links.into_iter().chain(new_links.into_iter()) {
for l in old_links
.into_iter()
.chain(new_links.into_iter())
.chain(short_links.into_iter())
{
if let Err(v) = msg.channel_id.send_message(&ctx, |m| match l.embed {
EmbedType::Beatmap(b, info, mods) => {
let t = handle_beatmap(&b, info, l.link, l.mode, mods, m);
@ -137,15 +146,9 @@ fn handle_new_links<'a>(ctx: &mut Context, content: &'a str) -> Result<Vec<ToPri
let mut to_prints: Vec<ToPrint<'a>> = Vec::new();
let cache = ctx.data.get_cloned::<BeatmapCache>();
for capture in NEW_LINK_REGEX.captures_iter(content) {
let mode = capture.name("mode").and_then(|v| {
Some(match v.as_str() {
"osu" => Mode::Std,
"taiko" => Mode::Taiko,
"fruits" => Mode::Catch,
"mania" => Mode::Mania,
_ => return None,
})
});
let mode = capture
.name("mode")
.and_then(|v| Mode::parse_from_new_site(v.as_str()));
let link = capture.get(0).unwrap().as_str();
let req = match capture.name("beatmap_id") {
Some(ref v) => BeatmapRequestKind::Beatmap(v.as_str().parse()?),
@ -163,8 +166,7 @@ fn handle_new_links<'a>(ctx: &mut Context, content: &'a str) -> Result<Vec<ToPri
// collect beatmap info
let mods = capture
.name("mods")
.map(|v| Mods::from_str(v.as_str()).ok())
.flatten()
.and_then(|v| Mods::from_str(v.as_str()).ok())
.unwrap_or(Mods::NOMOD);
let info = mode
.unwrap_or(beatmap.mode)
@ -192,6 +194,55 @@ fn handle_new_links<'a>(ctx: &mut Context, content: &'a str) -> Result<Vec<ToPri
Ok(to_prints)
}
fn handle_short_links<'a>(
ctx: &mut Context,
msg: &Message,
content: &'a str,
) -> Result<Vec<ToPrint<'a>>, Error> {
if let Some(guild_id) = msg.guild_id {
if announcer::announcer_of(ctx, crate::discord::announcer::ANNOUNCER_KEY, guild_id)?
!= Some(msg.channel_id)
{
// Disable if we are not in the server's announcer channel
return Ok(vec![]);
}
}
let osu = ctx.data.get_cloned::<BeatmapMetaCache>();
let cache = ctx.data.get_cloned::<BeatmapCache>();
Ok(SHORT_LINK_REGEX
.captures_iter(content)
.map(|capture| -> Result<_, Error> {
let mode = capture
.name("mode")
.and_then(|v| Mode::parse_from_new_site(v.as_str()));
let id: u64 = capture.name("id").unwrap().as_str().parse()?;
let beatmap = match mode {
Some(mode) => osu.get_beatmap(id, mode),
None => osu.get_beatmap_default(id),
}?;
let mods = capture
.name("mods")
.and_then(|v| Mods::from_str(v.as_str()).ok())
.unwrap_or(Mods::NOMOD);
let info = mode
.unwrap_or(beatmap.mode)
.to_oppai_mode()
.and_then(|mode| {
cache
.get_beatmap(beatmap.beatmap_id)
.and_then(|b| b.get_info_with(Some(mode), mods))
.ok()
});
Ok(ToPrint {
embed: EmbedType::Beatmap(beatmap, info, mods),
link: capture.get(0).unwrap().as_str(),
mode,
})
})
.filter_map(|v| v.ok())
.collect())
}
fn handle_beatmap<'a, 'b>(
beatmap: &Beatmap,
info: Option<BeatmapInfo>,

View file

@ -272,8 +272,12 @@ fn list_plays(plays: &[Score], mode: Mode, ctx: Context, m: &Message) -> Command
.map(|info| info.stars as f64)
.unwrap_or(b.difficulty.stars);
format!(
"[{:.1}*] {} - {} [{}] (#{})",
stars, b.artist, b.title, b.difficulty_name, b.beatmap_id
"[{:.1}*] {} - {} [{}] ({})",
stars,
b.artist,
b.title,
b.difficulty_name,
b.short_link(Some(mode), Some(plays[i].mods)),
)
} else {
"FETCH_FAILED".to_owned()
@ -554,18 +558,26 @@ pub fn top(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
fn get_user(ctx: &mut Context, msg: &Message, mut args: Args, mode: Mode) -> CommandResult {
let user = to_user_id_query(args.single::<UsernameArg>().ok(), &*ctx.data.read(), msg)?;
let osu = ctx.data.get_cloned::<OsuClient>();
let cache = ctx.data.get_cloned::<BeatmapMetaCache>();
let user = osu.user(user, |f| f.mode(mode))?;
let oppai = ctx.data.get_cloned::<BeatmapCache>();
match user {
Some(u) => {
let best = osu
.user_best(UserID::ID(u.id), |f| f.limit(1).mode(mode))?
.into_iter()
.next()
.map(|m| {
osu.beatmaps(BeatmapRequestKind::Beatmap(m.beatmap_id), |f| {
f.mode(mode, true)
.map(|m| -> Result<_, Error> {
let beatmap = cache.get_beatmap(m.beatmap_id, mode)?;
let info = mode
.to_oppai_mode()
.map(|mode| -> Result<_, Error> {
Ok(oppai
.get_beatmap(m.beatmap_id)?
.get_info_with(Some(mode), m.mods)?)
})
.map(|map| (m, BeatmapWithMode(map.into_iter().next().unwrap(), mode)))
.transpose()?;
Ok((m, BeatmapWithMode(beatmap, mode), info))
})
.transpose()?;
msg.channel_id.send_message(&ctx, |m| {

View file

@ -198,6 +198,27 @@ impl Mode {
_ => return None,
})
}
/// Parse from the new site's convention.
pub fn parse_from_new_site(s: &str) -> Option<Self> {
Some(match s {
"osu" => Mode::Std,
"taiko" => Mode::Taiko,
"fruits" => Mode::Catch,
"mania" => Mode::Mania,
_ => return None,
})
}
/// Returns the mode string in the new convention.
pub fn to_str_new_site(&self) -> &'static str {
match self {
Mode::Std => "osu",
Mode::Taiko => "taiko",
Mode::Catch => "fruits",
Mode::Mania => "mania",
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
@ -249,6 +270,19 @@ impl Beatmap {
)
}
/// Return a parsable short link.
pub fn short_link(&self, override_mode: Option<Mode>, mods: Option<Mods>) -> String {
format!(
"/b/{}{}{}",
self.beatmap_id,
match override_mode {
Some(mode) if mode != self.mode => format!("/{}", mode.to_str_new_site()),
_ => "".to_owned(),
},
mods.map(|m| format!("{}", m)).unwrap_or("".to_owned()),
)
}
/// Link to the cover image of the beatmap.
pub fn cover_url(&self) -> String {
format!(