This commit is contained in:
Natsu Kagami 2025-05-11 15:13:43 -04:00
parent 0661199420
commit 11e34e0c78
Signed by: nki
GPG key ID: 55A032EB38B49ADB
5 changed files with 78 additions and 65 deletions

View file

@ -303,20 +303,14 @@ async fn handle_listing<U: HasOsuEnv>(
cache::save_beatmap(&env, ctx.channel_id(), &beatmap).await?; cache::save_beatmap(&env, ctx.channel_id(), &beatmap).await?;
} }
Nth::All => { Nth::All => {
let reply = ctx let header = format!("Here are the {} plays by {}!", listing_kind, user.mention());
.clone() let reply = ctx.clone().reply(&header).await?;
.reply(format!(
"Here are the {} plays by {}!",
listing_kind,
user.mention()
))
.await?;
style style
.display_scores( .display_scores(
plays.try_collect::<Vec<_>>().await?, plays.try_collect::<Vec<_>>().await?,
ctx.clone().serenity_context(), ctx.clone().serenity_context(),
ctx.guild_id(), ctx.guild_id(),
(reply, ctx), (reply, ctx).with_header(header),
) )
.await?; .await?;
} }
@ -480,14 +474,12 @@ async fn check<U: HasOsuEnv>(
scores.reverse(); scores.reverse();
} }
let msg = ctx let header = format!(
.clone()
.reply(format!(
"Here are the plays by {} on {}!", "Here are the plays by {} on {}!",
args.user.mention(), args.user.mention(),
display display
)) );
.await?; let msg = ctx.clone().reply(&header).await?;
let style = style.unwrap_or(if scores.len() <= 5 { let style = style.unwrap_or(if scores.len() <= 5 {
ScoreListStyle::Grid ScoreListStyle::Grid
@ -500,7 +492,7 @@ async fn check<U: HasOsuEnv>(
scores, scores,
ctx.clone().serenity_context(), ctx.clone().serenity_context(),
ctx.guild_id(), ctx.guild_id(),
(msg, ctx), (msg, ctx).with_header(header),
) )
.await?; .await?;

View file

@ -2,6 +2,8 @@ pub use beatmapset::display_beatmapset;
pub use scores::ScoreListStyle; pub use scores::ScoreListStyle;
mod scores { mod scores {
use std::future::Future;
use poise::ChoiceParameter; use poise::ChoiceParameter;
use serenity::all::GuildId; use serenity::all::GuildId;
@ -41,7 +43,7 @@ mod scores {
impl ScoreListStyle { impl ScoreListStyle {
pub async fn display_scores( pub async fn display_scores(
self, self,
scores: Vec<Score>, scores: impl Future<Output = Result<Vec<Score>>>,
ctx: &Context, ctx: &Context,
guild_id: Option<GuildId>, guild_id: Option<GuildId>,
m: impl CanEdit, m: impl CanEdit,
@ -55,6 +57,8 @@ mod scores {
} }
mod grid { mod grid {
use std::future::Future;
use pagination::paginate_with_first_message; use pagination::paginate_with_first_message;
use serenity::all::{CreateActionRow, GuildId}; use serenity::all::{CreateActionRow, GuildId};
@ -65,7 +69,7 @@ mod scores {
use crate::models::Score; use crate::models::Score;
pub async fn display_scores_grid( pub async fn display_scores_grid(
scores: Vec<Score>, scores: impl Future<Output = Result<Vec<Score>>>,
ctx: &Context, ctx: &Context,
guild_id: Option<GuildId>, guild_id: Option<GuildId>,
mut on: impl CanEdit, mut on: impl CanEdit,
@ -81,7 +85,7 @@ mod scores {
paginate_with_first_message( paginate_with_first_message(
Paginate { Paginate {
env, env,
scores, scores: scores.await?,
guild_id, guild_id,
channel_id, channel_id,
}, },
@ -175,14 +179,22 @@ mod scores {
.await?; .await?;
return Ok(()); return Ok(());
} }
let header = on.headers().unwrap_or("").to_owned();
let content = format!("{}\n\nPreparing file...", header);
let first_edit = on
.apply_edit(CreateReply::default().content(content))
.map(|v| v.pls_ok());
let p = Paginate { let p = Paginate {
env: ctx.data.read().await.get::<OsuEnv>().unwrap().clone(), env: ctx.data.read().await.get::<OsuEnv>().unwrap().clone(),
header: on.get_message().await?.content.clone(), header: header.clone(),
scores, scores,
}; };
let content = p.to_table(0, p.scores.len()).await; let (_, content) = future::join(first_edit, p.to_table(0, p.scores.len())).await;
on.apply_edit( on.apply_edit(
CreateReply::default().attachment(CreateAttachment::bytes(content, "table.txt")), CreateReply::default()
.content(header)
.attachment(CreateAttachment::bytes(content, "table.md")),
) )
.await?; .await?;
Ok(()) Ok(())
@ -202,7 +214,7 @@ mod scores {
paginate_with_first_message( paginate_with_first_message(
Paginate { Paginate {
env: ctx.data.read().await.get::<OsuEnv>().unwrap().clone(), env: ctx.data.read().await.get::<OsuEnv>().unwrap().clone(),
header: on.get_message().await?.content.clone(), header: on.headers().unwrap_or("").to_owned(),
scores, scores,
}, },
ctx, ctx,

View file

@ -95,20 +95,21 @@ pub fn handle_check_button<'a>(
return Ok(()); return Ok(());
} }
comp.create_followup( let header = format!(
&ctx,
CreateInteractionResponseFollowup::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,
embed.mention() embed.mention()
)), );
comp.create_followup(
&ctx,
CreateInteractionResponseFollowup::new().content(&header),
) )
.await?; .await?;
let guild_id = comp.guild_id; let guild_id = comp.guild_id;
ScoreListStyle::Grid ScoreListStyle::Grid
.display_scores(scores, &ctx, guild_id, (comp, ctx)) .display_scores(scores, &ctx, guild_id, (comp, ctx).with_header(header))
.await .await
.pls_ok(); .pls_ok();
Ok(()) Ok(())

View file

@ -681,18 +681,14 @@ pub async fn recent(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu
let plays = osu_client.user_recent(UserID::ID(user.id), |f| f.mode(mode)); let plays = osu_client.user_recent(UserID::ID(user.id), |f| f.mode(mode));
match nth { match nth {
Nth::All => { Nth::All => {
let reply = msg let header = format!("Here are the recent plays by {}!", user.mention());
.reply( let reply = msg.reply(ctx, &header).await?;
ctx,
format!("Here are the recent plays by {}!", user.mention()),
)
.await?;
style style
.display_scores( .display_scores(
plays.try_collect::<Vec<_>>().await?, plays.try_collect::<Vec<_>>().await?,
ctx, ctx,
reply.guild_id, reply.guild_id,
(reply, ctx), (reply, ctx).with_header(header),
) )
.await?; .await?;
} }
@ -758,18 +754,14 @@ pub async fn pins(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
let plays = osu_client.user_pins(UserID::ID(user.id), |f| f.mode(mode)); let plays = osu_client.user_pins(UserID::ID(user.id), |f| f.mode(mode));
match nth { match nth {
Nth::All => { Nth::All => {
let reply = msg let header = format!("Here are the pinned plays by `{}`!", user.username);
.reply( let reply = msg.reply(ctx, &header).await?;
ctx,
format!("Here are the pinned plays by `{}`!", user.username),
)
.await?;
style style
.display_scores( .display_scores(
plays.try_collect::<Vec<_>>().await?, plays.try_collect::<Vec<_>>().await?,
ctx, ctx,
reply.guild_id, reply.guild_id,
(reply, ctx), (reply, ctx).with_header(header),
) )
.await?; .await?;
} }
@ -1017,18 +1009,14 @@ pub async fn check(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul
msg.reply(&ctx, "No scores found").await?; msg.reply(&ctx, "No scores found").await?;
return Ok(()); return Ok(());
} }
let reply = msg let header = format!(
.reply(
&ctx,
format!(
"Here are the scores by `{}` on {}!", "Here are the scores by `{}` on {}!",
&user.username, &user.username,
embed.mention() embed.mention()
), );
) let reply = msg.reply(&ctx, &header).await?;
.await?;
style style
.display_scores(scores, ctx, msg.guild_id, (reply, ctx)) .display_scores(scores, ctx, msg.guild_id, (reply, ctx).with_header(header))
.await?; .await?;
Ok(()) Ok(())
@ -1130,18 +1118,14 @@ pub async fn top(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
cache::save_beatmap(&env, msg.channel_id, &beatmap).await?; cache::save_beatmap(&env, msg.channel_id, &beatmap).await?;
} }
Nth::All => { Nth::All => {
let reply = msg let header = format!("Here are the top plays by {}!", user.mention());
.reply( let reply = msg.reply(&ctx, &header).await?;
&ctx,
format!("Here are the top plays by {}!", user.mention()),
)
.await?;
style style
.display_scores( .display_scores(
plays.try_collect::<Vec<_>>().await?, plays.try_collect::<Vec<_>>().await?,
ctx, ctx,
msg.guild_id, msg.guild_id,
(reply, ctx), (reply, ctx).with_header(header),
) )
.await?; .await?;
} }

View file

@ -14,9 +14,33 @@ const PREV: &str = "youmubot_pagination_prev";
const FAST_NEXT: &str = "youmubot_pagination_fast_next"; const FAST_NEXT: &str = "youmubot_pagination_fast_next";
const FAST_PREV: &str = "youmubot_pagination_fast_prev"; const FAST_PREV: &str = "youmubot_pagination_fast_prev";
pub trait CanEdit: Send { pub trait CanEdit: Send + Sized {
fn get_message(&self) -> impl Future<Output = Result<Message>> + Send; fn get_message(&self) -> impl Future<Output = Result<Message>> + Send;
fn apply_edit(&mut self, edit: CreateReply) -> impl Future<Output = Result<()>> + Send; fn apply_edit(&mut self, edit: CreateReply) -> impl Future<Output = Result<()>> + Send;
fn headers(&self) -> Option<&str> {
None
}
fn with_header(self, header: String) -> impl CanEdit {
WithHeaders(self, header)
}
}
struct WithHeaders<T>(T, String);
impl<T: CanEdit> CanEdit for WithHeaders<T> {
fn get_message(&self) -> impl Future<Output = Result<Message>> + Send {
self.0.get_message()
}
fn apply_edit(&mut self, edit: CreateReply) -> impl Future<Output = Result<()>> + Send {
self.0.apply_edit(edit)
}
fn headers(&self) -> Option<&str> {
Some(&self.1)
}
} }
impl<'a> CanEdit for (Message, &'a Context) { impl<'a> CanEdit for (Message, &'a Context) {