osu: Always load Me as a follow-up response

This commit is contained in:
Natsu Kagami 2024-09-25 18:30:07 +02:00
parent 76fd6c803d
commit 24e476239c
Signed by: nki
GPG key ID: 55A032EB38B49ADB
3 changed files with 73 additions and 75 deletions

View file

@ -2,8 +2,9 @@ use std::pin::Pin;
use future::Future; use future::Future;
use serenity::all::{ use serenity::all::{
ComponentInteractionDataKind, CreateActionRow, CreateButton, CreateInteractionResponse, ComponentInteraction, ComponentInteractionDataKind, CreateActionRow, CreateButton,
CreateInteractionResponseFollowup, CreateInteractionResponseMessage, GuildId, Interaction, CreateInteractionResponse, CreateInteractionResponseFollowup, CreateInteractionResponseMessage,
GuildId, Interaction,
}; };
use youmubot_prelude::*; use youmubot_prelude::*;
@ -52,14 +53,9 @@ pub fn handle_check_button<'a>(
interaction: &'a Interaction, interaction: &'a Interaction,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
let comp = match interaction.as_message_component() { let comp = match expect_and_defer_button(ctx, interaction, BTN_CHECK).await? {
Some(comp) Some(comp) => comp,
if comp.data.custom_id == BTN_CHECK None => return Ok(()),
&& matches!(comp.data.kind, ComponentInteractionDataKind::Button) =>
{
comp
}
_ => return Ok(()),
}; };
let msg = &*comp.message; let msg = &*comp.message;
@ -70,46 +66,46 @@ pub fn handle_check_button<'a>(
let user = match env.saved_users.by_user_id(comp.user.id).await? { let user = match env.saved_users.by_user_id(comp.user.id).await? {
Some(u) => u, Some(u) => u,
None => { None => {
comp.create_response(&ctx, CreateInteractionResponse::Message(CreateInteractionResponseMessage::new().content("You don't have a saved account yet! Save your osu! account by `y2!osu save <your-osu-username>`.").ephemeral(true))).await?; comp.create_followup(&ctx, CreateInteractionResponseFollowup::new().content("You don't have a saved account yet! Save your osu! account by `y2!osu save <your-osu-username>`.").ephemeral(true)).await?;
return Ok(()); return Ok(());
} }
}; };
let scores = super::do_check(&env, &bm, Mods::NOMOD, &crate::UserID::ID(user.id)).await?; let scores = super::do_check(&env, &bm, Mods::NOMOD, &crate::UserID::ID(user.id)).await?;
if scores.is_empty() { if scores.is_empty() {
comp.create_response( comp.create_followup(
&ctx, &ctx,
CreateInteractionResponse::Message( CreateInteractionResponseFollowup::new().content(format!(
CreateInteractionResponseMessage::new().content(format!(
"No plays found for [`{}`](<https://osu.ppy.sh/users/{}>) on `{}`.", "No plays found for [`{}`](<https://osu.ppy.sh/users/{}>) on `{}`.",
user.username, user.username,
user.id, user.id,
bm.short_link(Mods::NOMOD) bm.short_link(Mods::NOMOD)
)), )),
),
) )
.await?; .await?;
return Ok(()); return Ok(());
} }
let reply = { let reply = comp
comp.create_response( .create_followup(
&ctx, &ctx,
serenity::all::CreateInteractionResponse::Message( CreateInteractionResponseFollowup::new().content(format!(
CreateInteractionResponseMessage::new().content(format!(
"Here are the scores by [`{}`](<https://osu.ppy.sh/users/{}>) on `{}`!", "Here are the scores by [`{}`](<https://osu.ppy.sh/users/{}>) on `{}`!",
user.username, user.username,
user.id, user.id,
bm.short_link(Mods::NOMOD) bm.short_link(Mods::NOMOD)
)), )),
),
) )
.await?; .await?;
comp.get_response(&ctx).await?
}; let ctx = ctx.clone();
let guild_id = comp.guild_id;
spawn_future(async move {
ScoreListStyle::Grid ScoreListStyle::Grid
.display_scores(scores, bm.1, ctx, comp.guild_id, reply) .display_scores(scores, bm.1, &ctx, guild_id, reply)
.await?; .await
.pls_ok();
});
Ok(()) Ok(())
}) })
@ -129,14 +125,9 @@ pub fn handle_last_button<'a>(
interaction: &'a Interaction, interaction: &'a Interaction,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
let comp = match interaction.as_message_component() { let comp = match expect_and_defer_button(ctx, interaction, BTN_LAST).await? {
Some(comp) Some(comp) => comp,
if comp.data.custom_id == BTN_LAST None => return Ok(()),
&& matches!(comp.data.kind, ComponentInteractionDataKind::Button) =>
{
comp
}
_ => return Ok(()),
}; };
let msg = &*comp.message; let msg = &*comp.message;
@ -153,17 +144,15 @@ pub fn handle_last_button<'a>(
.get_beatmap(b.beatmap_id) .get_beatmap(b.beatmap_id)
.await? .await?
.get_possible_pp_with(*m, &mods)?; .get_possible_pp_with(*m, &mods)?;
comp.create_response( comp.create_followup(
&ctx, &ctx,
serenity::all::CreateInteractionResponse::Message( serenity::all::CreateInteractionResponseFollowup::new()
CreateInteractionResponseMessage::new()
.content(format!( .content(format!(
"Information for beatmap `{}`", "Information for beatmap `{}`",
bm.short_link(&mods) bm.short_link(&mods)
)) ))
.embed(beatmap_embed(b, *m, &mods, info)) .embed(beatmap_embed(b, *m, &mods, info))
.components(vec![beatmap_components(comp.guild_id)]), .components(vec![beatmap_components(comp.guild_id)]),
),
) )
.await?; .await?;
// Save the beatmap... // Save the beatmap...
@ -187,14 +176,9 @@ pub fn handle_lb_button<'a>(
interaction: &'a Interaction, interaction: &'a Interaction,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> { ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
Box::pin(async move { Box::pin(async move {
let comp = match interaction.as_message_component() { let comp = match expect_and_defer_button(ctx, interaction, BTN_LB).await? {
Some(comp) Some(comp) => comp,
if comp.data.custom_id == BTN_LB None => return Ok(()),
&& matches!(comp.data.kind, ComponentInteractionDataKind::Button) =>
{
comp
}
_ => return Ok(()),
}; };
let msg = &*comp.message; let msg = &*comp.message;
@ -206,11 +190,6 @@ pub fn handle_lb_button<'a>(
let order = OrderBy::default(); let order = OrderBy::default();
let guild = comp.guild_id.expect("Guild-only command"); let guild = comp.guild_id.expect("Guild-only command");
comp.create_response(
&ctx,
CreateInteractionResponse::Defer(CreateInteractionResponseMessage::new()),
)
.await?;
let scores = get_leaderboard(ctx, &env, &bm, order, guild).await?; let scores = get_leaderboard(ctx, &env, &bm, order, guild).await?;
if scores.is_empty() { if scores.is_empty() {
@ -237,3 +216,25 @@ pub fn handle_lb_button<'a>(
Ok(()) Ok(())
}) })
} }
async fn expect_and_defer_button<'a>(
ctx: &'_ Context,
interaction: &'a Interaction,
expect_id: &'static str,
) -> Result<Option<&'a ComponentInteraction>> {
let comp = match interaction.as_message_component() {
Some(comp)
if comp.data.custom_id == expect_id
&& matches!(comp.data.kind, ComponentInteractionDataKind::Button) =>
{
comp
}
_ => return Ok(None),
};
comp.create_response(
&ctx,
CreateInteractionResponse::Defer(CreateInteractionResponseMessage::new()),
)
.await?;
Ok(Some(comp))
}

View file

@ -26,13 +26,13 @@ where
/// InteractionHook represents the asynchronous hook that is run on every interaction. /// InteractionHook represents the asynchronous hook that is run on every interaction.
#[async_trait] #[async_trait]
pub trait InteractionHook: Send + Sync { pub trait InteractionHook: Send + Sync {
async fn call(&mut self, ctx: &Context, interaction: &Interaction) -> Result<()>; async fn call(&self, ctx: &Context, interaction: &Interaction) -> Result<()>;
} }
#[async_trait] #[async_trait]
impl<T> InteractionHook for T impl<T> InteractionHook for T
where where
T: for<'a> FnMut( T: for<'a> Fn(
&'a Context, &'a Context,
&'a Interaction, &'a Interaction,
) )
@ -40,7 +40,7 @@ where
+ Send + Send
+ Sync, + Sync,
{ {
async fn call(&mut self, ctx: &Context, interaction: &Interaction) -> Result<()> { async fn call(&self, ctx: &Context, interaction: &Interaction) -> Result<()> {
self(ctx, interaction).await self(ctx, interaction).await
} }
} }

View file

@ -21,7 +21,7 @@ mod compose_framework;
struct Handler { struct Handler {
hooks: Vec<RwLock<Box<dyn Hook>>>, hooks: Vec<RwLock<Box<dyn Hook>>>,
interaction_hooks: Vec<RwLock<Box<dyn InteractionHook>>>, interaction_hooks: Vec<Box<dyn InteractionHook>>,
ready_hooks: Vec<fn(&Context) -> CommandResult>, ready_hooks: Vec<fn(&Context) -> CommandResult>,
} }
@ -43,7 +43,7 @@ impl Handler {
} }
fn push_interaction_hook<T: InteractionHook + 'static>(&mut self, f: T) { fn push_interaction_hook<T: InteractionHook + 'static>(&mut self, f: T) {
self.interaction_hooks.push(RwLock::new(Box::new(f))); self.interaction_hooks.push(Box::new(f));
} }
} }
@ -113,10 +113,7 @@ impl EventHandler for Handler {
let interaction = &interaction; let interaction = &interaction;
self.interaction_hooks self.interaction_hooks
.iter() .iter()
.map(|hook| { .map(|hook| hook.call(ctx, interaction))
hook.write()
.then(|mut h| async move { h.call(ctx, interaction).await })
})
.collect::<stream::FuturesUnordered<_>>() .collect::<stream::FuturesUnordered<_>>()
.for_each(|v| async move { .for_each(|v| async move {
if let Err(e) = v { if let Err(e) = v {