mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-19 16:58:55 +00:00
osu: Streamify hook
This commit is contained in:
parent
26074ae257
commit
da2997d8f9
1 changed files with 180 additions and 130 deletions
|
@ -7,12 +7,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serenity::{
|
use serenity::{builder::CreateMessage, model::channel::Message, utils::MessageBuilder};
|
||||||
builder::CreateMessage,
|
|
||||||
framework::standard::{CommandError as Error, CommandResult},
|
|
||||||
model::channel::Message,
|
|
||||||
utils::MessageBuilder,
|
|
||||||
};
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use youmubot_prelude::*;
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
|
@ -30,43 +25,49 @@ lazy_static! {
|
||||||
).unwrap();
|
).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hook(ctx: &mut Context, msg: &Message) -> () {
|
pub async fn hook(ctx: &Context, msg: &Message) -> Result<()> {
|
||||||
if msg.author.bot {
|
if msg.author.bot {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
let mut v = move || -> CommandResult {
|
let (old_links, new_links, short_links) = (
|
||||||
let old_links = handle_old_links(ctx, &msg.content)?;
|
handle_old_links(ctx, &msg.content),
|
||||||
let new_links = handle_new_links(ctx, &msg.content)?;
|
handle_new_links(ctx, &msg.content),
|
||||||
let short_links = handle_short_links(ctx, &msg, &msg.content)?;
|
handle_short_links(ctx, &msg, &msg.content),
|
||||||
let mut last_beatmap = None;
|
);
|
||||||
for l in old_links
|
let last_beatmap = stream::select(old_links, stream::select(new_links, short_links))
|
||||||
.into_iter()
|
.then(|l| async move {
|
||||||
.chain(new_links.into_iter())
|
let mut bm: Option<super::BeatmapWithMode> = None;
|
||||||
.chain(short_links.into_iter())
|
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);
|
||||||
let mode = l.mode.unwrap_or(b.mode);
|
let mode = l.mode.unwrap_or(b.mode);
|
||||||
last_beatmap = Some(super::BeatmapWithMode(b, mode));
|
bm = Some(super::BeatmapWithMode(b, mode));
|
||||||
t
|
t
|
||||||
}
|
}
|
||||||
EmbedType::Beatmapset(b) => handle_beatmapset(b, l.link, l.mode, m),
|
EmbedType::Beatmapset(b) => handle_beatmapset(b, l.link, l.mode, m),
|
||||||
}) {
|
})
|
||||||
println!("Error in osu! hook: {:?}", v)
|
.await?;
|
||||||
|
let r: Result<_> = Ok(bm);
|
||||||
|
r
|
||||||
|
})
|
||||||
|
.filter_map(|v| async move {
|
||||||
|
match v {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.fold(None, |_, v| async move { Some(v) })
|
||||||
|
.await;
|
||||||
|
|
||||||
// Save the beatmap for query later.
|
// Save the beatmap for query later.
|
||||||
if let Some(t) = last_beatmap {
|
if let Some(t) = last_beatmap {
|
||||||
if let Err(v) = super::cache::save_beatmap(&*ctx.data.read(), msg.channel_id, &t) {
|
super::cache::save_beatmap(&*ctx.data.read().await, msg.channel_id, &t)?;
|
||||||
dbg!(v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
|
||||||
if let Err(v) = v() {
|
|
||||||
println!("Error in osu! hook: {:?}", v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum EmbedType {
|
enum EmbedType {
|
||||||
|
@ -80,16 +81,21 @@ struct ToPrint<'a> {
|
||||||
mode: Option<Mode>,
|
mode: Option<Mode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_old_links<'a>(ctx: &mut Context, content: &'a str) -> Result<Vec<ToPrint<'a>>, Error> {
|
fn handle_old_links<'a>(
|
||||||
let osu = ctx.data.get_cloned::<OsuClient>();
|
ctx: &'a Context,
|
||||||
let mut to_prints: Vec<ToPrint<'a>> = Vec::new();
|
content: &'a str,
|
||||||
let cache = ctx.data.get_cloned::<BeatmapCache>();
|
) -> impl stream::Stream<Item = ToPrint<'a>> + 'a {
|
||||||
for capture in OLD_LINK_REGEX.captures_iter(content) {
|
OLD_LINK_REGEX
|
||||||
|
.captures_iter(content)
|
||||||
|
.map(move |capture| async move {
|
||||||
|
let data = ctx.data.read().await;
|
||||||
|
let osu = data.get::<OsuClient>().unwrap();
|
||||||
|
let cache = data.get::<BeatmapCache>().unwrap();
|
||||||
let req_type = capture.name("link_type").unwrap().as_str();
|
let req_type = capture.name("link_type").unwrap().as_str();
|
||||||
let req = match req_type {
|
let req = match req_type {
|
||||||
"b" => BeatmapRequestKind::Beatmap(capture["id"].parse()?),
|
"b" => BeatmapRequestKind::Beatmap(capture["id"].parse()?),
|
||||||
"s" => BeatmapRequestKind::Beatmapset(capture["id"].parse()?),
|
"s" => BeatmapRequestKind::Beatmapset(capture["id"].parse()?),
|
||||||
_ => continue,
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let mode = capture
|
let mode = capture
|
||||||
.name("mode")
|
.name("mode")
|
||||||
|
@ -104,13 +110,18 @@ fn handle_old_links<'a>(ctx: &mut Context, content: &'a str) -> Result<Vec<ToPri
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
let beatmaps = osu.beatmaps(req, |v| match mode {
|
let beatmaps = osu
|
||||||
|
.beatmaps(req, |v| match mode {
|
||||||
Some(m) => v.mode(m, true),
|
Some(m) => v.mode(m, true),
|
||||||
None => v,
|
None => v,
|
||||||
})?;
|
})
|
||||||
match req_type {
|
.await?;
|
||||||
|
if beatmaps.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let r: Result<_> = Ok(match req_type {
|
||||||
"b" => {
|
"b" => {
|
||||||
for b in beatmaps.into_iter() {
|
let b = beatmaps.into_iter().next().unwrap();
|
||||||
// collect beatmap info
|
// collect beatmap info
|
||||||
let mods = capture
|
let mods = capture
|
||||||
.name("mods")
|
.name("mods")
|
||||||
|
@ -123,46 +134,65 @@ fn handle_old_links<'a>(ctx: &mut Context, content: &'a str) -> Result<Vec<ToPri
|
||||||
.and_then(|b| b.get_info_with(Some(mode), mods))
|
.and_then(|b| b.get_info_with(Some(mode), mods))
|
||||||
.ok()
|
.ok()
|
||||||
});
|
});
|
||||||
to_prints.push(ToPrint {
|
Some(ToPrint {
|
||||||
embed: EmbedType::Beatmap(b, info, mods),
|
embed: EmbedType::Beatmap(b, info, mods),
|
||||||
link: capture.get(0).unwrap().as_str(),
|
link: capture.get(0).unwrap().as_str(),
|
||||||
mode,
|
mode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
"s" => Some(ToPrint {
|
||||||
"s" => to_prints.push(ToPrint {
|
|
||||||
embed: EmbedType::Beatmapset(beatmaps),
|
embed: EmbedType::Beatmapset(beatmaps),
|
||||||
link: capture.get(0).unwrap().as_str(),
|
link: capture.get(0).unwrap().as_str(),
|
||||||
mode,
|
mode,
|
||||||
}),
|
}),
|
||||||
_ => (),
|
_ => None,
|
||||||
|
});
|
||||||
|
r
|
||||||
|
})
|
||||||
|
.collect::<stream::FuturesUnordered<_>>()
|
||||||
|
.filter_map(|v| {
|
||||||
|
future::ready(match v {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
Ok(to_prints)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_new_links<'a>(ctx: &mut Context, content: &'a str) -> Result<Vec<ToPrint<'a>>, Error> {
|
fn handle_new_links<'a>(
|
||||||
let osu = ctx.data.get_cloned::<OsuClient>();
|
ctx: &'a Context,
|
||||||
let mut to_prints: Vec<ToPrint<'a>> = Vec::new();
|
content: &'a str,
|
||||||
let cache = ctx.data.get_cloned::<BeatmapCache>();
|
) -> impl stream::Stream<Item = ToPrint<'a>> + 'a {
|
||||||
for capture in NEW_LINK_REGEX.captures_iter(content) {
|
NEW_LINK_REGEX
|
||||||
|
.captures_iter(content)
|
||||||
|
.map(|capture| async move {
|
||||||
|
let data = ctx.data.read().await;
|
||||||
|
let osu = data.get::<OsuClient>().unwrap();
|
||||||
|
let cache = data.get::<BeatmapCache>().unwrap();
|
||||||
let mode = capture
|
let mode = capture
|
||||||
.name("mode")
|
.name("mode")
|
||||||
.and_then(|v| Mode::parse_from_new_site(v.as_str()));
|
.and_then(|v| Mode::parse_from_new_site(v.as_str()));
|
||||||
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()?),
|
||||||
None => {
|
None => BeatmapRequestKind::Beatmapset(
|
||||||
BeatmapRequestKind::Beatmapset(capture.name("set_id").unwrap().as_str().parse()?)
|
capture.name("set_id").unwrap().as_str().parse()?,
|
||||||
}
|
),
|
||||||
};
|
};
|
||||||
let beatmaps = osu.beatmaps(req, |v| match mode {
|
let beatmaps = osu
|
||||||
|
.beatmaps(req, |v| match mode {
|
||||||
Some(m) => v.mode(m, true),
|
Some(m) => v.mode(m, true),
|
||||||
None => v,
|
None => v,
|
||||||
})?;
|
})
|
||||||
match capture.name("beatmap_id") {
|
.await?;
|
||||||
|
if beatmaps.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
let r: Result<_> = Ok(match capture.name("beatmap_id") {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
for beatmap in beatmaps.into_iter() {
|
let beatmap = beatmaps.into_iter().next().unwrap();
|
||||||
// collect beatmap info
|
// collect beatmap info
|
||||||
let mods = capture
|
let mods = capture
|
||||||
.name("mods")
|
.name("mods")
|
||||||
|
@ -177,48 +207,59 @@ fn handle_new_links<'a>(ctx: &mut Context, content: &'a str) -> Result<Vec<ToPri
|
||||||
.and_then(|b| b.get_info_with(Some(mode), mods))
|
.and_then(|b| b.get_info_with(Some(mode), mods))
|
||||||
.ok()
|
.ok()
|
||||||
});
|
});
|
||||||
to_prints.push(ToPrint {
|
Some(ToPrint {
|
||||||
embed: EmbedType::Beatmap(beatmap, info, mods),
|
embed: EmbedType::Beatmap(beatmap, info, mods),
|
||||||
link,
|
link,
|
||||||
mode,
|
mode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
None => Some(ToPrint {
|
||||||
None => to_prints.push(ToPrint {
|
|
||||||
embed: EmbedType::Beatmapset(beatmaps),
|
embed: EmbedType::Beatmapset(beatmaps),
|
||||||
link,
|
link,
|
||||||
mode,
|
mode,
|
||||||
}),
|
}),
|
||||||
|
});
|
||||||
|
r
|
||||||
|
})
|
||||||
|
.collect::<stream::FuturesUnordered<_>>()
|
||||||
|
.filter_map(|v| {
|
||||||
|
future::ready(match v {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
Ok(to_prints)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_short_links<'a>(
|
fn handle_short_links<'a>(
|
||||||
ctx: &mut Context,
|
ctx: &'a Context,
|
||||||
msg: &Message,
|
msg: &'a Message,
|
||||||
content: &'a str,
|
content: &'a str,
|
||||||
) -> Result<Vec<ToPrint<'a>>, Error> {
|
) -> impl stream::Stream<Item = ToPrint<'a>> + 'a {
|
||||||
|
SHORT_LINK_REGEX
|
||||||
|
.captures_iter(content)
|
||||||
|
.map(|capture| async move {
|
||||||
if let Some(guild_id) = msg.guild_id {
|
if let Some(guild_id) = msg.guild_id {
|
||||||
if announcer::announcer_of(ctx, crate::discord::announcer::ANNOUNCER_KEY, guild_id)?
|
if announcer::announcer_of(ctx, crate::discord::announcer::ANNOUNCER_KEY, guild_id)
|
||||||
|
.await?
|
||||||
!= Some(msg.channel_id)
|
!= Some(msg.channel_id)
|
||||||
{
|
{
|
||||||
// Disable if we are not in the server's announcer channel
|
// Disable if we are not in the server's announcer channel
|
||||||
return Ok(vec![]);
|
return Err(Error::msg("not in server announcer channel"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let osu = ctx.data.get_cloned::<BeatmapMetaCache>();
|
let data = ctx.data.read().await;
|
||||||
let cache = ctx.data.get_cloned::<BeatmapCache>();
|
let osu = data.get::<BeatmapMetaCache>().unwrap();
|
||||||
Ok(SHORT_LINK_REGEX
|
let cache = data.get::<BeatmapCache>().unwrap();
|
||||||
.captures_iter(content)
|
|
||||||
.map(|capture| -> Result<_, Error> {
|
|
||||||
let mode = capture
|
let mode = capture
|
||||||
.name("mode")
|
.name("mode")
|
||||||
.and_then(|v| Mode::parse_from_new_site(v.as_str()));
|
.and_then(|v| Mode::parse_from_new_site(v.as_str()));
|
||||||
let id: u64 = capture.name("id").unwrap().as_str().parse()?;
|
let id: u64 = capture.name("id").unwrap().as_str().parse()?;
|
||||||
let beatmap = match mode {
|
let beatmap = match mode {
|
||||||
Some(mode) => osu.get_beatmap(id, mode),
|
Some(mode) => osu.get_beatmap(id, mode).await,
|
||||||
None => osu.get_beatmap_default(id),
|
None => osu.get_beatmap_default(id).await,
|
||||||
}?;
|
}?;
|
||||||
let mods = capture
|
let mods = capture
|
||||||
.name("mods")
|
.name("mods")
|
||||||
|
@ -233,14 +274,23 @@ fn handle_short_links<'a>(
|
||||||
.and_then(|b| b.get_info_with(Some(mode), mods))
|
.and_then(|b| b.get_info_with(Some(mode), mods))
|
||||||
.ok()
|
.ok()
|
||||||
});
|
});
|
||||||
Ok(ToPrint {
|
let r: Result<_> = Ok(ToPrint {
|
||||||
embed: EmbedType::Beatmap(beatmap, info, mods),
|
embed: EmbedType::Beatmap(beatmap, info, mods),
|
||||||
link: capture.get(0).unwrap().as_str(),
|
link: capture.get(0).unwrap().as_str(),
|
||||||
mode,
|
mode,
|
||||||
|
});
|
||||||
|
r
|
||||||
|
})
|
||||||
|
.collect::<stream::FuturesUnordered<_>>()
|
||||||
|
.filter_map(|v| {
|
||||||
|
future::ready(match v {
|
||||||
|
Ok(v) => Some(v),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.filter_map(|v| v.ok())
|
|
||||||
.collect())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_beatmap<'a, 'b>(
|
fn handle_beatmap<'a, 'b>(
|
||||||
|
|
Loading…
Add table
Reference in a new issue