mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-19 16:58:55 +00:00
Implement a score hook
This commit is contained in:
parent
7a98dc21a9
commit
7f15a4698a
3 changed files with 92 additions and 10 deletions
|
@ -2,6 +2,7 @@ use super::BeatmapWithMode;
|
||||||
use crate::{
|
use crate::{
|
||||||
discord::oppai_cache::{Accuracy, BeatmapContent, BeatmapInfo, BeatmapInfoWithPP},
|
discord::oppai_cache::{Accuracy, BeatmapContent, BeatmapInfo, BeatmapInfoWithPP},
|
||||||
models::{Beatmap, Difficulty, Mode, Mods, Rank, Score, User},
|
models::{Beatmap, Difficulty, Mode, Mods, Rank, Score, User},
|
||||||
|
UserHeader,
|
||||||
};
|
};
|
||||||
use serenity::{
|
use serenity::{
|
||||||
all::CreateAttachment,
|
all::CreateAttachment,
|
||||||
|
@ -243,7 +244,7 @@ pub(crate) struct ScoreEmbedBuilder<'a> {
|
||||||
s: &'a Score,
|
s: &'a Score,
|
||||||
bm: &'a BeatmapWithMode,
|
bm: &'a BeatmapWithMode,
|
||||||
content: &'a BeatmapContent,
|
content: &'a BeatmapContent,
|
||||||
u: &'a User,
|
u: UserHeader,
|
||||||
top_record: Option<u8>,
|
top_record: Option<u8>,
|
||||||
world_record: Option<u16>,
|
world_record: Option<u16>,
|
||||||
footer: Option<String>,
|
footer: Option<String>,
|
||||||
|
@ -268,13 +269,13 @@ pub(crate) fn score_embed<'a>(
|
||||||
s: &'a Score,
|
s: &'a Score,
|
||||||
bm: &'a BeatmapWithMode,
|
bm: &'a BeatmapWithMode,
|
||||||
content: &'a BeatmapContent,
|
content: &'a BeatmapContent,
|
||||||
u: &'a User,
|
u: impl Into<UserHeader>,
|
||||||
) -> ScoreEmbedBuilder<'a> {
|
) -> ScoreEmbedBuilder<'a> {
|
||||||
ScoreEmbedBuilder {
|
ScoreEmbedBuilder {
|
||||||
s,
|
s,
|
||||||
bm,
|
bm,
|
||||||
content,
|
content,
|
||||||
u,
|
u: u.into(),
|
||||||
top_record: None,
|
top_record: None,
|
||||||
world_record: None,
|
world_record: None,
|
||||||
footer: None,
|
footer: None,
|
||||||
|
@ -288,7 +289,7 @@ impl<'a> ScoreEmbedBuilder<'a> {
|
||||||
let b = &self.bm.0;
|
let b = &self.bm.0;
|
||||||
let s = self.s;
|
let s = self.s;
|
||||||
let content = self.content;
|
let content = self.content;
|
||||||
let u = self.u;
|
let u = &self.u;
|
||||||
let accuracy = s.accuracy(mode);
|
let accuracy = s.accuracy(mode);
|
||||||
let info = content.get_info_with(mode, s.mods).ok();
|
let info = content.get_info_with(mode, s.mods).ok();
|
||||||
let stars = info
|
let stars = info
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use futures_util::stream::FuturesOrdered;
|
||||||
|
use itertools::Itertools;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use pagination::paginate_from_fn;
|
use pagination::paginate_from_fn;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serenity::all::EditMessage;
|
use serenity::{
|
||||||
use serenity::{builder::CreateMessage, model::channel::Message, utils::MessageBuilder};
|
all::{EditMessage, EMBED_MAX_COUNT},
|
||||||
|
builder::CreateMessage,
|
||||||
|
model::channel::Message,
|
||||||
|
utils::MessageBuilder,
|
||||||
|
};
|
||||||
|
|
||||||
use youmubot_prelude::*;
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
use crate::discord::OsuEnv;
|
use crate::discord::embeds::score_embed;
|
||||||
|
use crate::discord::{BeatmapWithMode, OsuEnv};
|
||||||
use crate::{
|
use crate::{
|
||||||
discord::oppai_cache::BeatmapInfoWithPP,
|
discord::oppai_cache::BeatmapInfoWithPP,
|
||||||
models::{Beatmap, Mode, Mods},
|
models::{Beatmap, Mode, Mods},
|
||||||
|
@ -18,6 +25,7 @@ use crate::{
|
||||||
use super::embeds::beatmap_embed;
|
use super::embeds::beatmap_embed;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
// Beatmap(set) hooks
|
||||||
pub(crate) static ref OLD_LINK_REGEX: Regex = Regex::new(
|
pub(crate) static ref OLD_LINK_REGEX: Regex = Regex::new(
|
||||||
r"(?:https?://)?osu\.ppy\.sh/(?P<link_type>s|b)/(?P<id>\d+)(?:[\&\?]m=(?P<mode>\d))?(?:\+(?P<mods>[A-Z]+))?"
|
r"(?:https?://)?osu\.ppy\.sh/(?P<link_type>s|b)/(?P<id>\d+)(?:[\&\?]m=(?P<mode>\d))?(?:\+(?P<mods>[A-Z]+))?"
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
@ -27,8 +35,81 @@ lazy_static! {
|
||||||
pub(crate) static ref SHORT_LINK_REGEX: Regex = Regex::new(
|
pub(crate) static ref SHORT_LINK_REGEX: Regex = Regex::new(
|
||||||
r"(?:^|\s|\W)(?P<main>/b/(?P<id>\d+)(?:/(?P<mode>osu|taiko|fruits|mania))?(?:\+(?P<mods>[A-Z]+))?)"
|
r"(?:^|\s|\W)(?P<main>/b/(?P<id>\d+)(?:/(?P<mode>osu|taiko|fruits|mania))?(?:\+(?P<mods>[A-Z]+))?)"
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
|
// Score hook
|
||||||
|
pub(crate) static ref SCORE_LINK_REGEX: Regex = Regex::new(
|
||||||
|
r"(?:https?://)?osu\.ppy\.sh/scores/(?P<score_id>\d+)"
|
||||||
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// React to /scores/{id} links.
|
||||||
|
pub fn score_hook<'a>(
|
||||||
|
ctx: &'a Context,
|
||||||
|
msg: &'a Message,
|
||||||
|
) -> std::pin::Pin<Box<dyn future::Future<Output = Result<()>> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
if msg.author.bot {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let env = {
|
||||||
|
let data = ctx.data.read().await;
|
||||||
|
data.get::<OsuEnv>().unwrap().clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let scores = SCORE_LINK_REGEX
|
||||||
|
.captures_iter(&msg.content)
|
||||||
|
.filter_map(|caps| caps.name("score_id"))
|
||||||
|
.filter_map(|score_id| score_id.as_str().parse::<u64>().ok())
|
||||||
|
.map(|id| env.client.score(id))
|
||||||
|
.collect::<FuturesOrdered<_>>()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|score| score.pls_ok().flatten());
|
||||||
|
|
||||||
|
let embed_chunks = scores
|
||||||
|
.map(|score| async move {
|
||||||
|
let env = {
|
||||||
|
let data = ctx.data.read().await;
|
||||||
|
data.get::<OsuEnv>().unwrap().clone()
|
||||||
|
};
|
||||||
|
let bm = env
|
||||||
|
.beatmaps
|
||||||
|
.get_beatmap(score.beatmap_id, score.mode)
|
||||||
|
.await?;
|
||||||
|
let content = env.oppai.get_beatmap(score.beatmap_id).await?;
|
||||||
|
let header = env.client.user_header(score.user_id).await?.unwrap();
|
||||||
|
Ok(score_embed(&score, &BeatmapWithMode(bm, score.mode), &content, header).build())
|
||||||
|
as Result<_>
|
||||||
|
})
|
||||||
|
.collect::<FuturesOrdered<_>>()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|v| v.pls_ok())
|
||||||
|
.chunks(EMBED_MAX_COUNT)
|
||||||
|
.into_iter()
|
||||||
|
.map(|chunk| chunk.collect::<Vec<_>>())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for embeds in embed_chunks {
|
||||||
|
msg.channel_id
|
||||||
|
.send_message(
|
||||||
|
&ctx,
|
||||||
|
CreateMessage::new()
|
||||||
|
.reference_message(msg)
|
||||||
|
.content("Here are the scores mentioned in the message!")
|
||||||
|
.embeds(embeds),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.pls_ok();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// React to .osz and .osu uploads.
|
||||||
pub fn dot_osu_hook<'a>(
|
pub fn dot_osu_hook<'a>(
|
||||||
ctx: &'a Context,
|
ctx: &'a Context,
|
||||||
msg: &'a Message,
|
msg: &'a Message,
|
||||||
|
|
|
@ -16,7 +16,7 @@ use serenity::{
|
||||||
use db::{OsuLastBeatmap, OsuSavedUsers, OsuUser, OsuUserBests};
|
use db::{OsuLastBeatmap, OsuSavedUsers, OsuUser, OsuUserBests};
|
||||||
use embeds::{beatmap_embed, score_embed, user_embed};
|
use embeds::{beatmap_embed, score_embed, user_embed};
|
||||||
use hook::SHORT_LINK_REGEX;
|
use hook::SHORT_LINK_REGEX;
|
||||||
pub use hook::{dot_osu_hook, hook};
|
pub use hook::{dot_osu_hook, hook, score_hook};
|
||||||
use server_rank::{SERVER_RANK_COMMAND, SHOW_LEADERBOARD_COMMAND};
|
use server_rank::{SERVER_RANK_COMMAND, SHOW_LEADERBOARD_COMMAND};
|
||||||
use youmubot_prelude::announcer::AnnouncerHandler;
|
use youmubot_prelude::announcer::AnnouncerHandler;
|
||||||
use youmubot_prelude::{stream::FuturesUnordered, *};
|
use youmubot_prelude::{stream::FuturesUnordered, *};
|
||||||
|
@ -44,7 +44,7 @@ mod server_rank;
|
||||||
pub(crate) struct OsuClient;
|
pub(crate) struct OsuClient;
|
||||||
|
|
||||||
impl TypeMapKey for OsuClient {
|
impl TypeMapKey for OsuClient {
|
||||||
type Value = Arc<OsuHttpClient>;
|
type Value = Arc<crate::Client>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The environment for osu! app commands.
|
/// The environment for osu! app commands.
|
||||||
|
@ -56,7 +56,7 @@ pub struct OsuEnv {
|
||||||
pub(crate) last_beatmaps: OsuLastBeatmap,
|
pub(crate) last_beatmaps: OsuLastBeatmap,
|
||||||
pub(crate) user_bests: OsuUserBests,
|
pub(crate) user_bests: OsuUserBests,
|
||||||
// clients
|
// clients
|
||||||
pub(crate) client: Arc<OsuHttpClient>,
|
pub(crate) client: Arc<crate::Client>,
|
||||||
pub(crate) oppai: BeatmapCache,
|
pub(crate) oppai: BeatmapCache,
|
||||||
pub(crate) beatmaps: BeatmapMetaCache,
|
pub(crate) beatmaps: BeatmapMetaCache,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue