mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-16 07:18:54 +00:00
Add short link references in embeds
This commit is contained in:
parent
5c270db9cb
commit
cafa65581c
4 changed files with 140 additions and 27 deletions
|
@ -109,7 +109,11 @@ pub fn beatmap_embed<'a>(
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.push_line(format!(" [[Beatmapset]]({})", b.beatmapset_link()))
|
.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("Language: ")
|
||||||
.push_bold(&b.language)
|
.push_bold(&b.language)
|
||||||
.push(" | Genre: ")
|
.push(" | Genre: ")
|
||||||
|
@ -209,7 +213,11 @@ pub fn beatmapset_embed<'a>(
|
||||||
(
|
(
|
||||||
format!("[{}]", b.difficulty_name),
|
format!("[{}]", b.difficulty_name),
|
||||||
MessageBuilder::new()
|
MessageBuilder::new()
|
||||||
.push(format!("[[Link]]({})", b.link()))
|
.push(format!(
|
||||||
|
"[[Link]]({}) (`{}`)",
|
||||||
|
b.link(),
|
||||||
|
b.short_link(m, None)
|
||||||
|
))
|
||||||
.push(", ")
|
.push(", ")
|
||||||
.push_bold(format!("{:.2}⭐", b.difficulty.stars))
|
.push_bold(format!("{:.2}⭐", b.difficulty.stars))
|
||||||
.push(", ")
|
.push(", ")
|
||||||
|
@ -311,7 +319,11 @@ pub(crate) fn score_embed<'a>(
|
||||||
.field(
|
.field(
|
||||||
"Map stats",
|
"Map stats",
|
||||||
MessageBuilder::new()
|
MessageBuilder::new()
|
||||||
.push(format!("[[Link]]({})", b.link()))
|
.push(format!(
|
||||||
|
"[[Link]]({}) (`{}`)",
|
||||||
|
b.link(),
|
||||||
|
b.short_link(Some(mode), Some(s.mods))
|
||||||
|
))
|
||||||
.push(", ")
|
.push(", ")
|
||||||
.push_bold(format!("{:.2}⭐", stars))
|
.push_bold(format!("{:.2}⭐", stars))
|
||||||
.push(", ")
|
.push(", ")
|
||||||
|
@ -346,7 +358,7 @@ pub(crate) fn score_embed<'a>(
|
||||||
|
|
||||||
pub(crate) fn user_embed<'a>(
|
pub(crate) fn user_embed<'a>(
|
||||||
u: User,
|
u: User,
|
||||||
best: Option<(Score, BeatmapWithMode)>,
|
best: Option<(Score, BeatmapWithMode, Option<BeatmapInfo>)>,
|
||||||
m: &'a mut CreateEmbed,
|
m: &'a mut CreateEmbed,
|
||||||
) -> &'a mut CreateEmbed {
|
) -> &'a mut CreateEmbed {
|
||||||
m.title(u.username)
|
m.title(u.username)
|
||||||
|
@ -388,8 +400,8 @@ pub(crate) fn user_embed<'a>(
|
||||||
),
|
),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.fields(best.map(|(v, map)| {
|
.fields(best.map(|(v, map, info)| {
|
||||||
let map = map.0;
|
let BeatmapWithMode(map, mode) = map;
|
||||||
(
|
(
|
||||||
"Best Record",
|
"Best Record",
|
||||||
MessageBuilder::new()
|
MessageBuilder::new()
|
||||||
|
@ -413,8 +425,12 @@ pub(crate) fn user_embed<'a>(
|
||||||
MessageBuilder::new().push_bold_safe(&map.title).build(),
|
MessageBuilder::new().push_bold_safe(&map.title).build(),
|
||||||
map.link()
|
map.link()
|
||||||
))
|
))
|
||||||
.push(format!(" [{}]", map.difficulty_name))
|
.push_line(format!(" [{}]", map.difficulty_name))
|
||||||
.push(format!(" ({:.1}⭐)", map.difficulty.stars))
|
.push(format!(
|
||||||
|
"{:.1}⭐ | `{}`",
|
||||||
|
info.map(|i| i.stars as f64).unwrap_or(map.difficulty.stars),
|
||||||
|
map.short_link(Some(mode), Some(v.mods))
|
||||||
|
))
|
||||||
.build(),
|
.build(),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::OsuClient;
|
use super::OsuClient;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
discord::beatmap_cache::BeatmapMetaCache,
|
||||||
discord::oppai_cache::{BeatmapCache, BeatmapInfo},
|
discord::oppai_cache::{BeatmapCache, BeatmapInfo},
|
||||||
models::{Beatmap, Mode, Mods},
|
models::{Beatmap, Mode, Mods},
|
||||||
request::BeatmapRequestKind,
|
request::BeatmapRequestKind,
|
||||||
|
@ -24,6 +25,9 @@ lazy_static! {
|
||||||
static ref NEW_LINK_REGEX: Regex = Regex::new(
|
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]+))?"
|
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();
|
).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) -> () {
|
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 mut v = move || -> CommandResult {
|
||||||
let old_links = handle_old_links(ctx, &msg.content)?;
|
let old_links = handle_old_links(ctx, &msg.content)?;
|
||||||
let new_links = handle_new_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;
|
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 {
|
if let Err(v) = msg.channel_id.send_message(&ctx, |m| match l.embed {
|
||||||
EmbedType::Beatmap(b, info, mods) => {
|
EmbedType::Beatmap(b, info, mods) => {
|
||||||
let t = handle_beatmap(&b, info, l.link, l.mode, mods, m);
|
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 mut to_prints: Vec<ToPrint<'a>> = Vec::new();
|
||||||
let cache = ctx.data.get_cloned::<BeatmapCache>();
|
let cache = ctx.data.get_cloned::<BeatmapCache>();
|
||||||
for capture in NEW_LINK_REGEX.captures_iter(content) {
|
for capture in NEW_LINK_REGEX.captures_iter(content) {
|
||||||
let mode = capture.name("mode").and_then(|v| {
|
let mode = capture
|
||||||
Some(match v.as_str() {
|
.name("mode")
|
||||||
"osu" => Mode::Std,
|
.and_then(|v| Mode::parse_from_new_site(v.as_str()));
|
||||||
"taiko" => Mode::Taiko,
|
|
||||||
"fruits" => Mode::Catch,
|
|
||||||
"mania" => Mode::Mania,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
let link = capture.get(0).unwrap().as_str();
|
let link = capture.get(0).unwrap().as_str();
|
||||||
let req = match capture.name("beatmap_id") {
|
let req = match capture.name("beatmap_id") {
|
||||||
Some(ref v) => BeatmapRequestKind::Beatmap(v.as_str().parse()?),
|
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
|
// collect beatmap info
|
||||||
let mods = capture
|
let mods = capture
|
||||||
.name("mods")
|
.name("mods")
|
||||||
.map(|v| Mods::from_str(v.as_str()).ok())
|
.and_then(|v| Mods::from_str(v.as_str()).ok())
|
||||||
.flatten()
|
|
||||||
.unwrap_or(Mods::NOMOD);
|
.unwrap_or(Mods::NOMOD);
|
||||||
let info = mode
|
let info = mode
|
||||||
.unwrap_or(beatmap.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)
|
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>(
|
fn handle_beatmap<'a, 'b>(
|
||||||
beatmap: &Beatmap,
|
beatmap: &Beatmap,
|
||||||
info: Option<BeatmapInfo>,
|
info: Option<BeatmapInfo>,
|
||||||
|
|
|
@ -272,8 +272,12 @@ fn list_plays(plays: &[Score], mode: Mode, ctx: Context, m: &Message) -> Command
|
||||||
.map(|info| info.stars as f64)
|
.map(|info| info.stars as f64)
|
||||||
.unwrap_or(b.difficulty.stars);
|
.unwrap_or(b.difficulty.stars);
|
||||||
format!(
|
format!(
|
||||||
"[{:.1}*] {} - {} [{}] (#{})",
|
"[{:.1}*] {} - {} [{}] ({})",
|
||||||
stars, b.artist, b.title, b.difficulty_name, b.beatmap_id
|
stars,
|
||||||
|
b.artist,
|
||||||
|
b.title,
|
||||||
|
b.difficulty_name,
|
||||||
|
b.short_link(Some(mode), Some(plays[i].mods)),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
"FETCH_FAILED".to_owned()
|
"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 {
|
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 user = to_user_id_query(args.single::<UsernameArg>().ok(), &*ctx.data.read(), msg)?;
|
||||||
let osu = ctx.data.get_cloned::<OsuClient>();
|
let osu = ctx.data.get_cloned::<OsuClient>();
|
||||||
|
let cache = ctx.data.get_cloned::<BeatmapMetaCache>();
|
||||||
let user = osu.user(user, |f| f.mode(mode))?;
|
let user = osu.user(user, |f| f.mode(mode))?;
|
||||||
|
let oppai = ctx.data.get_cloned::<BeatmapCache>();
|
||||||
match user {
|
match user {
|
||||||
Some(u) => {
|
Some(u) => {
|
||||||
let best = osu
|
let best = osu
|
||||||
.user_best(UserID::ID(u.id), |f| f.limit(1).mode(mode))?
|
.user_best(UserID::ID(u.id), |f| f.limit(1).mode(mode))?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.next()
|
.next()
|
||||||
.map(|m| {
|
.map(|m| -> Result<_, Error> {
|
||||||
osu.beatmaps(BeatmapRequestKind::Beatmap(m.beatmap_id), |f| {
|
let beatmap = cache.get_beatmap(m.beatmap_id, mode)?;
|
||||||
f.mode(mode, true)
|
let info = mode
|
||||||
})
|
.to_oppai_mode()
|
||||||
.map(|map| (m, BeatmapWithMode(map.into_iter().next().unwrap(), mode)))
|
.map(|mode| -> Result<_, Error> {
|
||||||
|
Ok(oppai
|
||||||
|
.get_beatmap(m.beatmap_id)?
|
||||||
|
.get_info_with(Some(mode), m.mods)?)
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
Ok((m, BeatmapWithMode(beatmap, mode), info))
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
msg.channel_id.send_message(&ctx, |m| {
|
msg.channel_id.send_message(&ctx, |m| {
|
||||||
|
|
|
@ -198,6 +198,27 @@ impl Mode {
|
||||||
_ => return None,
|
_ => 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)]
|
#[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.
|
/// Link to the cover image of the beatmap.
|
||||||
pub fn cover_url(&self) -> String {
|
pub fn cover_url(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
|
|
Loading…
Add table
Reference in a new issue