mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-09 20:20:29 +00:00
pagination: make editing generic over a CanEdit interface
This commit is contained in:
parent
5fde2f343a
commit
a35563801d
11 changed files with 233 additions and 207 deletions
|
@ -3,7 +3,7 @@ use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||||
use codeforces::Contest;
|
use codeforces::Contest;
|
||||||
use pagination::paginate_from_fn;
|
use pagination::paginate_from_fn;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
builder::{CreateMessage, EditMessage},
|
builder::CreateMessage,
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
macros::{command, group},
|
macros::{command, group},
|
||||||
Args, CommandResult,
|
Args, CommandResult,
|
||||||
|
@ -180,7 +180,7 @@ pub async fn ranks(ctx: &Context, m: &Message) -> CommandResult {
|
||||||
let last_updated = ranks.iter().map(|(_, cfu)| cfu.last_update).min().unwrap();
|
let last_updated = ranks.iter().map(|(_, cfu)| cfu.last_update).min().unwrap();
|
||||||
|
|
||||||
paginate_reply(
|
paginate_reply(
|
||||||
paginate_from_fn(move |page, _, _, btns| {
|
paginate_from_fn(move |page, btns| {
|
||||||
use Align::*;
|
use Align::*;
|
||||||
let ranks = ranks.clone();
|
let ranks = ranks.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
|
@ -222,7 +222,9 @@ pub async fn ranks(ctx: &Context, m: &Message) -> CommandResult {
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Ok(Some(EditMessage::new().content(content).components(btns)))
|
Ok(Some(
|
||||||
|
CreateReply::default().content(content).components(btns),
|
||||||
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.with_page_count(total_pages),
|
.with_page_count(total_pages),
|
||||||
|
@ -317,7 +319,7 @@ pub(crate) async fn contest_rank_table(
|
||||||
let ranks = Arc::new(ranks);
|
let ranks = Arc::new(ranks);
|
||||||
|
|
||||||
paginate_reply(
|
paginate_reply(
|
||||||
paginate_from_fn(move |page, _, _, btns| {
|
paginate_from_fn(move |page, btns| {
|
||||||
let contest = contest.clone();
|
let contest = contest.clone();
|
||||||
let problems = problems.clone();
|
let problems = problems.clone();
|
||||||
let ranks = ranks.clone();
|
let ranks = ranks.clone();
|
||||||
|
@ -388,7 +390,9 @@ pub(crate) async fn contest_rank_table(
|
||||||
.push_line(format!("Page **{}/{}**", page + 1, total_pages))
|
.push_line(format!("Page **{}/{}**", page + 1, total_pages))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Ok(Some(EditMessage::new().content(content).components(btns)))
|
Ok(Some(
|
||||||
|
CreateReply::default().content(content).components(btns),
|
||||||
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.with_page_count(total_pages),
|
.with_page_count(total_pages),
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use serenity::{
|
use serenity::{
|
||||||
builder::EditMessage,
|
|
||||||
framework::standard::{macros::command, Args, CommandResult},
|
framework::standard::{macros::command, Args, CommandResult},
|
||||||
model::{
|
model::{
|
||||||
channel::{Message, ReactionType},
|
channel::{Message, ReactionType},
|
||||||
|
@ -44,7 +43,7 @@ async fn list(ctx: &Context, m: &Message, _: Args) -> CommandResult {
|
||||||
let pages = (roles.len() + ROLES_PER_PAGE - 1) / ROLES_PER_PAGE;
|
let pages = (roles.len() + ROLES_PER_PAGE - 1) / ROLES_PER_PAGE;
|
||||||
|
|
||||||
paginate_reply(
|
paginate_reply(
|
||||||
paginate_from_fn(|page, _, _, btns| {
|
paginate_from_fn(|page, btns| {
|
||||||
let roles = roles.clone();
|
let roles = roles.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let page = page as usize;
|
let page = page as usize;
|
||||||
|
@ -77,7 +76,9 @@ async fn list(ctx: &Context, m: &Message, _: Args) -> CommandResult {
|
||||||
.push_line(format!("Page **{}/{}**", page + 1, pages))
|
.push_line(format!("Page **{}/{}**", page + 1, pages))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Ok(Some(EditMessage::new().content(content).components(btns)))
|
Ok(Some(
|
||||||
|
CreateReply::default().content(content).components(btns),
|
||||||
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.with_page_count(pages),
|
.with_page_count(pages),
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serenity::builder::EditMessage;
|
|
||||||
use serenity::framework::standard::CommandError as Error;
|
use serenity::framework::standard::CommandError as Error;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
framework::standard::{
|
framework::standard::{
|
||||||
|
@ -66,7 +65,7 @@ async fn message_command(
|
||||||
}
|
}
|
||||||
let images = std::sync::Arc::new(images);
|
let images = std::sync::Arc::new(images);
|
||||||
paginate_reply(
|
paginate_reply(
|
||||||
paginate_from_fn(|page, _, _, btns| {
|
paginate_from_fn(|page, btns| {
|
||||||
let images = images.clone();
|
let images = images.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let page = page as usize;
|
let page = page as usize;
|
||||||
|
@ -74,7 +73,7 @@ async fn message_command(
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
EditMessage::new()
|
CreateReply::default()
|
||||||
.content(format!(
|
.content(format!(
|
||||||
"[🖼️ **{}/{}**] Here's the image you requested!\n\n{}",
|
"[🖼️ **{}/{}**] Here's the image you requested!\n\n{}",
|
||||||
page + 1,
|
page + 1,
|
||||||
|
|
|
@ -294,11 +294,14 @@ async fn handle_listing<U: HasOsuEnv>(
|
||||||
listing_kind,
|
listing_kind,
|
||||||
user.mention()
|
user.mention()
|
||||||
))
|
))
|
||||||
.await?
|
|
||||||
.into_message()
|
|
||||||
.await?;
|
.await?;
|
||||||
style
|
style
|
||||||
.display_scores(plays, ctx.serenity_context(), ctx.guild_id(), reply)
|
.display_scores(
|
||||||
|
plays,
|
||||||
|
ctx.clone().serenity_context(),
|
||||||
|
ctx.guild_id(),
|
||||||
|
(reply, ctx),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,16 +371,14 @@ async fn beatmap<U: HasOsuEnv>(
|
||||||
let msg = ctx
|
let msg = ctx
|
||||||
.clone()
|
.clone()
|
||||||
.reply(format!("Information for {}", b0.beatmapset_mention()))
|
.reply(format!("Information for {}", b0.beatmapset_mention()))
|
||||||
.await?
|
|
||||||
.into_message()
|
|
||||||
.await?;
|
.await?;
|
||||||
display_beatmapset(
|
display_beatmapset(
|
||||||
ctx.serenity_context().clone(),
|
ctx.clone().serenity_context(),
|
||||||
vec,
|
vec,
|
||||||
mode,
|
mode,
|
||||||
mods,
|
mods,
|
||||||
ctx.guild_id(),
|
ctx.guild_id(),
|
||||||
msg,
|
(msg, ctx),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
@ -470,8 +471,6 @@ async fn check<U: HasOsuEnv>(
|
||||||
args.user.mention(),
|
args.user.mention(),
|
||||||
display
|
display
|
||||||
))
|
))
|
||||||
.await?
|
|
||||||
.into_message()
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let style = style.unwrap_or(if scores.len() <= 5 {
|
let style = style.unwrap_or(if scores.len() <= 5 {
|
||||||
|
@ -481,7 +480,12 @@ async fn check<U: HasOsuEnv>(
|
||||||
});
|
});
|
||||||
|
|
||||||
style
|
style
|
||||||
.display_scores(scores, ctx.serenity_context(), ctx.guild_id(), msg)
|
.display_scores(
|
||||||
|
scores,
|
||||||
|
ctx.clone().serenity_context(),
|
||||||
|
ctx.guild_id(),
|
||||||
|
(msg, ctx),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -577,13 +581,13 @@ async fn leaderboard<U: HasOsuEnv>(
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
ScoreListStyle::Grid => {
|
ScoreListStyle::Grid => {
|
||||||
let reply = ctx.reply(header).await?.into_message().await?;
|
let reply = ctx.reply(header).await?;
|
||||||
style
|
style
|
||||||
.display_scores(
|
.display_scores(
|
||||||
scores.into_iter().map(|s| s.score).collect(),
|
scores.into_iter().map(|s| s.score).collect(),
|
||||||
ctx.serenity_context(),
|
ctx.serenity_context(),
|
||||||
Some(guild.id),
|
Some(guild.id),
|
||||||
reply,
|
(reply, ctx),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ pub use scores::ScoreListStyle;
|
||||||
|
|
||||||
mod scores {
|
mod scores {
|
||||||
use poise::ChoiceParameter;
|
use poise::ChoiceParameter;
|
||||||
use serenity::{all::GuildId, model::channel::Message};
|
use serenity::all::GuildId;
|
||||||
|
|
||||||
use youmubot_prelude::*;
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ mod scores {
|
||||||
scores: Vec<Score>,
|
scores: Vec<Score>,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
guild_id: Option<GuildId>,
|
guild_id: Option<GuildId>,
|
||||||
m: Message,
|
m: impl CanEdit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
ScoreListStyle::Table => table::display_scores_table(scores, ctx, m).await,
|
ScoreListStyle::Table => table::display_scores_table(scores, ctx, m).await,
|
||||||
|
@ -54,8 +54,6 @@ mod scores {
|
||||||
mod grid {
|
mod grid {
|
||||||
use pagination::paginate_with_first_message;
|
use pagination::paginate_with_first_message;
|
||||||
use serenity::all::{CreateActionRow, GuildId};
|
use serenity::all::{CreateActionRow, GuildId};
|
||||||
use serenity::builder::EditMessage;
|
|
||||||
use serenity::model::channel::Message;
|
|
||||||
|
|
||||||
use youmubot_prelude::*;
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
|
@ -67,16 +65,23 @@ mod scores {
|
||||||
scores: Vec<Score>,
|
scores: Vec<Score>,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
guild_id: Option<GuildId>,
|
guild_id: Option<GuildId>,
|
||||||
mut on: Message,
|
mut on: impl CanEdit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone();
|
||||||
|
let channel_id = on.get_message().await?.channel_id;
|
||||||
if scores.is_empty() {
|
if scores.is_empty() {
|
||||||
on.edit(&ctx, EditMessage::new().content("No plays found"))
|
on.apply_edit(CreateReply::default().content("No plays found"))
|
||||||
.await?;
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
paginate_with_first_message(
|
paginate_with_first_message(
|
||||||
Paginate { scores, guild_id },
|
Paginate {
|
||||||
|
env,
|
||||||
|
scores,
|
||||||
|
guild_id,
|
||||||
|
channel_id,
|
||||||
|
},
|
||||||
ctx,
|
ctx,
|
||||||
on,
|
on,
|
||||||
std::time::Duration::from_secs(60),
|
std::time::Duration::from_secs(60),
|
||||||
|
@ -86,8 +91,10 @@ mod scores {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Paginate {
|
pub struct Paginate {
|
||||||
|
env: OsuEnv,
|
||||||
scores: Vec<Score>,
|
scores: Vec<Score>,
|
||||||
guild_id: Option<GuildId>,
|
guild_id: Option<GuildId>,
|
||||||
|
channel_id: serenity::all::ChannelId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -95,11 +102,9 @@ mod scores {
|
||||||
async fn render(
|
async fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
page: u8,
|
page: u8,
|
||||||
ctx: &Context,
|
|
||||||
msg: &Message,
|
|
||||||
btns: Vec<CreateActionRow>,
|
btns: Vec<CreateActionRow>,
|
||||||
) -> Result<Option<EditMessage>> {
|
) -> Result<Option<CreateReply>> {
|
||||||
let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone();
|
let env = &self.env;
|
||||||
let page = page as usize;
|
let page = page as usize;
|
||||||
let score = &self.scores[page];
|
let score = &self.scores[page];
|
||||||
|
|
||||||
|
@ -120,9 +125,9 @@ mod scores {
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| Error::msg("user not found"))?;
|
.ok_or_else(|| Error::msg("user not found"))?;
|
||||||
|
|
||||||
save_beatmap(&env, msg.channel_id, &bm).await?;
|
save_beatmap(&env, self.channel_id, &bm).await?;
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
EditMessage::new()
|
CreateReply::default()
|
||||||
.embed({
|
.embed({
|
||||||
crate::discord::embeds::score_embed(score, &bm, &content, &user)
|
crate::discord::embeds::score_embed(score, &bm, &content, &user)
|
||||||
.footer(format!("Page {}/{}", page + 1, self.scores.len()))
|
.footer(format!("Page {}/{}", page + 1, self.scores.len()))
|
||||||
|
@ -148,8 +153,6 @@ mod scores {
|
||||||
|
|
||||||
use pagination::paginate_with_first_message;
|
use pagination::paginate_with_first_message;
|
||||||
use serenity::all::CreateActionRow;
|
use serenity::all::CreateActionRow;
|
||||||
use serenity::builder::EditMessage;
|
|
||||||
use serenity::model::channel::Message;
|
|
||||||
|
|
||||||
use youmubot_prelude::table_format::Align::{Left, Right};
|
use youmubot_prelude::table_format::Align::{Left, Right};
|
||||||
use youmubot_prelude::table_format::{table_formatting, Align};
|
use youmubot_prelude::table_format::{table_formatting, Align};
|
||||||
|
@ -162,17 +165,18 @@ mod scores {
|
||||||
pub async fn display_scores_table(
|
pub async fn display_scores_table(
|
||||||
scores: Vec<Score>,
|
scores: Vec<Score>,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
mut on: Message,
|
mut on: impl CanEdit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if scores.is_empty() {
|
if scores.is_empty() {
|
||||||
on.edit(&ctx, EditMessage::new().content("No plays found"))
|
on.apply_edit(CreateReply::default().content("No plays found"))
|
||||||
.await?;
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
paginate_with_first_message(
|
paginate_with_first_message(
|
||||||
Paginate {
|
Paginate {
|
||||||
header: on.content.clone(),
|
env: ctx.data.read().await.get::<OsuEnv>().unwrap().clone(),
|
||||||
|
header: on.get_message().await?.content.clone(),
|
||||||
scores,
|
scores,
|
||||||
},
|
},
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -184,6 +188,7 @@ mod scores {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Paginate {
|
pub struct Paginate {
|
||||||
|
env: OsuEnv,
|
||||||
header: String,
|
header: String,
|
||||||
scores: Vec<Score>,
|
scores: Vec<Score>,
|
||||||
}
|
}
|
||||||
|
@ -201,11 +206,9 @@ mod scores {
|
||||||
async fn render(
|
async fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
page: u8,
|
page: u8,
|
||||||
ctx: &Context,
|
|
||||||
_: &Message,
|
|
||||||
btns: Vec<CreateActionRow>,
|
btns: Vec<CreateActionRow>,
|
||||||
) -> Result<Option<EditMessage>> {
|
) -> Result<Option<CreateReply>> {
|
||||||
let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone();
|
let env = &self.env;
|
||||||
|
|
||||||
let meta_cache = &env.beatmaps;
|
let meta_cache = &env.beatmaps;
|
||||||
let oppai = &env.oppai;
|
let oppai = &env.oppai;
|
||||||
|
@ -331,7 +334,9 @@ mod scores {
|
||||||
.push_line("[?] means pp was predicted by oppai-rs.")
|
.push_line("[?] means pp was predicted by oppai-rs.")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Ok(Some(EditMessage::new().content(content).components(btns)))
|
Ok(Some(
|
||||||
|
CreateReply::default().content(content).components(btns),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn len(&self) -> Option<usize> {
|
fn len(&self) -> Option<usize> {
|
||||||
|
@ -344,8 +349,8 @@ mod scores {
|
||||||
mod beatmapset {
|
mod beatmapset {
|
||||||
use serenity::{
|
use serenity::{
|
||||||
all::{CreateActionRow, CreateButton, GuildId},
|
all::{CreateActionRow, CreateButton, GuildId},
|
||||||
builder::{CreateEmbedFooter, EditMessage},
|
builder::CreateEmbedFooter,
|
||||||
model::channel::{Message, ReactionType},
|
model::channel::ReactionType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use youmubot_prelude::*;
|
use youmubot_prelude::*;
|
||||||
|
@ -363,12 +368,12 @@ mod beatmapset {
|
||||||
const SHOW_ALL: &str = "youmubot_osu::discord::display::show_all";
|
const SHOW_ALL: &str = "youmubot_osu::discord::display::show_all";
|
||||||
|
|
||||||
pub async fn display_beatmapset(
|
pub async fn display_beatmapset(
|
||||||
ctx: Context,
|
ctx: &Context,
|
||||||
mut beatmapset: Vec<Beatmap>,
|
mut beatmapset: Vec<Beatmap>,
|
||||||
mode: Option<Mode>,
|
mode: Option<Mode>,
|
||||||
mods: Option<UnparsedMods>,
|
mods: Option<UnparsedMods>,
|
||||||
guild_id: Option<GuildId>,
|
guild_id: Option<GuildId>,
|
||||||
target: Message,
|
target: impl CanEdit,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
assert!(!beatmapset.is_empty(), "Beatmapset should not be empty");
|
assert!(!beatmapset.is_empty(), "Beatmapset should not be empty");
|
||||||
|
|
||||||
|
@ -381,6 +386,8 @@ mod beatmapset {
|
||||||
});
|
});
|
||||||
|
|
||||||
let p = Paginate {
|
let p = Paginate {
|
||||||
|
env: ctx.data.read().await.get::<OsuEnv>().unwrap().clone(),
|
||||||
|
channel_id: target.get_message().await?.channel_id,
|
||||||
infos: vec![None; beatmapset.len()],
|
infos: vec![None; beatmapset.len()],
|
||||||
maps: beatmapset,
|
maps: beatmapset,
|
||||||
mode,
|
mode,
|
||||||
|
@ -389,20 +396,20 @@ mod beatmapset {
|
||||||
};
|
};
|
||||||
|
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
spawn_future(async move {
|
pagination::paginate_with_first_message(
|
||||||
pagination::paginate_with_first_message(
|
p,
|
||||||
p,
|
&ctx,
|
||||||
&ctx,
|
target,
|
||||||
target,
|
std::time::Duration::from_secs(60),
|
||||||
std::time::Duration::from_secs(60),
|
)
|
||||||
)
|
.await
|
||||||
.await
|
.pls_ok();
|
||||||
.pls_ok();
|
|
||||||
});
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Paginate {
|
struct Paginate {
|
||||||
|
env: OsuEnv,
|
||||||
|
channel_id: serenity::all::ChannelId,
|
||||||
maps: Vec<Beatmap>,
|
maps: Vec<Beatmap>,
|
||||||
infos: Vec<Option<BeatmapInfoWithPP>>,
|
infos: Vec<Option<BeatmapInfoWithPP>>,
|
||||||
mode: Option<Mode>,
|
mode: Option<Mode>,
|
||||||
|
@ -411,15 +418,9 @@ mod beatmapset {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Paginate {
|
impl Paginate {
|
||||||
async fn get_beatmap_info(
|
async fn get_beatmap_info(&self, b: &Beatmap, mods: &Mods) -> Result<BeatmapInfoWithPP> {
|
||||||
&self,
|
self.env
|
||||||
ctx: &Context,
|
.oppai
|
||||||
b: &Beatmap,
|
|
||||||
mods: &Mods,
|
|
||||||
) -> Result<BeatmapInfoWithPP> {
|
|
||||||
let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone();
|
|
||||||
|
|
||||||
env.oppai
|
|
||||||
.get_beatmap(b.beatmap_id)
|
.get_beatmap(b.beatmap_id)
|
||||||
.await
|
.await
|
||||||
.map(move |v| v.get_possible_pp_with(b.mode.with_override(self.mode), &mods))
|
.map(move |v| v.get_possible_pp_with(b.mode.with_override(self.mode), &mods))
|
||||||
|
@ -435,14 +436,12 @@ mod beatmapset {
|
||||||
async fn render(
|
async fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
page: u8,
|
page: u8,
|
||||||
ctx: &Context,
|
|
||||||
msg: &Message,
|
|
||||||
btns: Vec<CreateActionRow>,
|
btns: Vec<CreateActionRow>,
|
||||||
) -> Result<Option<EditMessage>> {
|
) -> Result<Option<CreateReply>> {
|
||||||
let page = page as usize;
|
let page = page as usize;
|
||||||
if page == self.maps.len() {
|
if page == self.maps.len() {
|
||||||
return Ok(Some(
|
return Ok(Some(
|
||||||
EditMessage::new()
|
CreateReply::default()
|
||||||
.embed(crate::discord::embeds::beatmapset_embed(
|
.embed(crate::discord::embeds::beatmapset_embed(
|
||||||
&self.maps[..],
|
&self.maps[..],
|
||||||
self.mode,
|
self.mode,
|
||||||
|
@ -464,21 +463,20 @@ mod beatmapset {
|
||||||
let info = match &self.infos[page] {
|
let info = match &self.infos[page] {
|
||||||
Some(info) => info,
|
Some(info) => info,
|
||||||
None => {
|
None => {
|
||||||
let info = self.get_beatmap_info(ctx, map, &mods).await?;
|
let info = self.get_beatmap_info(map, &mods).await?;
|
||||||
self.infos[page].insert(info)
|
self.infos[page].insert(info)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let env = ctx.data.read().await.get::<OsuEnv>().unwrap().clone();
|
|
||||||
save_beatmap(
|
save_beatmap(
|
||||||
&env,
|
&self.env,
|
||||||
msg.channel_id,
|
self.channel_id,
|
||||||
&BeatmapWithMode(map.clone(), self.mode),
|
&BeatmapWithMode(map.clone(), self.mode),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.pls_ok();
|
.pls_ok();
|
||||||
|
|
||||||
Ok(Some(
|
Ok(Some(
|
||||||
EditMessage::new().embed(
|
CreateReply::default().embed(
|
||||||
crate::discord::embeds::beatmap_embed(
|
crate::discord::embeds::beatmap_embed(
|
||||||
map,
|
map,
|
||||||
self.mode.unwrap_or(map.mode),
|
self.mode.unwrap_or(map.mode),
|
||||||
|
@ -512,16 +510,16 @@ mod beatmapset {
|
||||||
async fn handle_reaction(
|
async fn handle_reaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
page: u8,
|
page: u8,
|
||||||
ctx: &Context,
|
_ctx: &Context,
|
||||||
message: &mut serenity::model::channel::Message,
|
message: &mut impl CanEdit,
|
||||||
reaction: &str,
|
reaction: &str,
|
||||||
) -> Result<Option<u8>> {
|
) -> Result<Option<u8>> {
|
||||||
// Render the old style.
|
// Render the old style.
|
||||||
if reaction == SHOW_ALL {
|
if reaction == SHOW_ALL {
|
||||||
pagination::do_render(self, self.maps.len() as u8, ctx, message).await?;
|
pagination::do_render(self, self.maps.len() as u8, message).await?;
|
||||||
return Ok(Some(self.maps.len() as u8));
|
return Ok(Some(self.maps.len() as u8));
|
||||||
}
|
}
|
||||||
pagination::handle_pagination_reaction(page, self, ctx, message, reaction)
|
pagination::handle_pagination_reaction(page, self, message, reaction)
|
||||||
.await
|
.await
|
||||||
.map(Some)
|
.map(Some)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use futures_util::stream::FuturesOrdered;
|
use futures_util::stream::FuturesOrdered;
|
||||||
use pagination::paginate_from_fn;
|
use pagination::paginate_from_fn;
|
||||||
use serenity::{
|
use serenity::{builder::CreateMessage, model::channel::Message, utils::MessageBuilder};
|
||||||
all::EditMessage, builder::CreateMessage, model::channel::Message, utils::MessageBuilder,
|
|
||||||
};
|
|
||||||
|
|
||||||
use stream::Stream;
|
use stream::Stream;
|
||||||
use youmubot_prelude::*;
|
use youmubot_prelude::*;
|
||||||
|
@ -192,16 +190,16 @@ pub fn dot_osu_hook<'a>(
|
||||||
} else {
|
} else {
|
||||||
let osu_embeds = Arc::new(osu_embeds);
|
let osu_embeds = Arc::new(osu_embeds);
|
||||||
paginate_reply(
|
paginate_reply(
|
||||||
paginate_from_fn(|page, _, _, btns| {
|
paginate_from_fn(|page, btns| {
|
||||||
let osu_embeds = osu_embeds.clone();
|
let osu_embeds = osu_embeds.clone();
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let (embed, attachments) = &osu_embeds[page as usize];
|
let (embed, attachments) = &osu_embeds[page as usize];
|
||||||
let mut edit = EditMessage::new()
|
let mut edit = CreateReply::default()
|
||||||
.content(format!("Attached beatmaps ({}/{})", page + 1, embed_len))
|
.content(format!("Attached beatmaps ({}/{})", page + 1, embed_len))
|
||||||
.embed(embed.clone())
|
.embed(embed.clone())
|
||||||
.components(btns);
|
.components(btns);
|
||||||
for att in attachments {
|
for att in attachments {
|
||||||
edit = edit.new_attachment(att.clone());
|
edit = edit.attachment(att.clone());
|
||||||
}
|
}
|
||||||
Ok(Some(edit))
|
Ok(Some(edit))
|
||||||
})
|
})
|
||||||
|
@ -334,12 +332,12 @@ async fn handle_beatmapset<'a, 'b>(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
crate::discord::display::display_beatmapset(
|
crate::discord::display::display_beatmapset(
|
||||||
ctx.clone(),
|
ctx,
|
||||||
beatmaps,
|
beatmaps,
|
||||||
mode,
|
mode,
|
||||||
None,
|
None,
|
||||||
reply_to.guild_id,
|
reply_to.guild_id,
|
||||||
reply,
|
(reply, ctx),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.pls_ok();
|
.pls_ok();
|
||||||
|
|
|
@ -95,27 +95,22 @@ pub fn handle_check_button<'a>(
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let reply = comp
|
comp.create_followup(
|
||||||
.create_followup(
|
&ctx,
|
||||||
&ctx,
|
CreateInteractionResponseFollowup::new().content(format!(
|
||||||
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()
|
)),
|
||||||
)),
|
)
|
||||||
)
|
.await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
let ctx = ctx.clone();
|
|
||||||
let guild_id = comp.guild_id;
|
let guild_id = comp.guild_id;
|
||||||
spawn_future(async move {
|
ScoreListStyle::Grid
|
||||||
ScoreListStyle::Grid
|
.display_scores(scores, &ctx, guild_id, (comp, ctx))
|
||||||
.display_scores(scores, &ctx, guild_id, reply)
|
.await
|
||||||
.await
|
.pls_ok();
|
||||||
.pls_ok();
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -326,24 +321,21 @@ async fn handle_last_req(
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let content_type = format!("Information for {}", embed.mention());
|
||||||
match embed {
|
match embed {
|
||||||
EmbedType::Beatmapset(beatmapset, mode) => {
|
EmbedType::Beatmapset(beatmapset, mode) => {
|
||||||
let reply = comp
|
comp.create_followup(
|
||||||
.create_followup(
|
&ctx,
|
||||||
&ctx,
|
CreateInteractionResponseFollowup::new().content(content_type),
|
||||||
CreateInteractionResponseFollowup::new().content(format!(
|
)
|
||||||
"Beatmapset `{}`",
|
.await?;
|
||||||
beatmapset[0].beatmapset_mention()
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
super::display::display_beatmapset(
|
super::display::display_beatmapset(
|
||||||
ctx.clone(),
|
ctx,
|
||||||
beatmapset,
|
beatmapset,
|
||||||
mode,
|
mode,
|
||||||
None,
|
None,
|
||||||
comp.guild_id,
|
comp.guild_id,
|
||||||
reply,
|
(comp, ctx),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -357,7 +349,7 @@ async fn handle_last_req(
|
||||||
comp.create_followup(
|
comp.create_followup(
|
||||||
&ctx,
|
&ctx,
|
||||||
serenity::all::CreateInteractionResponseFollowup::new()
|
serenity::all::CreateInteractionResponseFollowup::new()
|
||||||
.content(format!("Information for beatmap {}", b.mention(m, &mods)))
|
.content(content_type)
|
||||||
.embed(beatmap_embed(&*b, m.unwrap_or(b.mode), &mods, &info))
|
.embed(beatmap_embed(&*b, m.unwrap_or(b.mode), &mods, &info))
|
||||||
.components(vec![beatmap_components(m.unwrap_or(b.mode), comp.guild_id)]),
|
.components(vec![beatmap_components(m.unwrap_or(b.mode), comp.guild_id)]),
|
||||||
)
|
)
|
||||||
|
|
|
@ -689,7 +689,7 @@ pub async fn recent(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
style
|
style
|
||||||
.display_scores(plays, ctx, reply.guild_id, reply)
|
.display_scores(plays, ctx, reply.guild_id, (reply, ctx))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
Nth::Nth(nth) => {
|
Nth::Nth(nth) => {
|
||||||
|
@ -762,7 +762,7 @@ pub async fn pins(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
style
|
style
|
||||||
.display_scores(plays, ctx, reply.guild_id, reply)
|
.display_scores(plays, ctx, reply.guild_id, (reply, ctx))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
Nth::Nth(nth) => {
|
Nth::Nth(nth) => {
|
||||||
|
@ -966,7 +966,7 @@ pub async fn last(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
let reply = msg
|
let reply = msg
|
||||||
.reply(&ctx, format!("Information for {}", content_type))
|
.reply(&ctx, format!("Information for {}", content_type))
|
||||||
.await?;
|
.await?;
|
||||||
display::display_beatmapset(ctx.clone(), beatmaps, mode, umods, msg.guild_id, reply)
|
display::display_beatmapset(ctx, beatmaps, mode, umods, msg.guild_id, (reply, ctx))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1018,7 +1018,7 @@ pub async fn check(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
style
|
style
|
||||||
.display_scores(scores, ctx, msg.guild_id, reply)
|
.display_scores(scores, ctx, msg.guild_id, (reply, ctx))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1130,7 +1130,7 @@ pub async fn top(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
style
|
style
|
||||||
.display_scores(plays, ctx, msg.guild_id, reply)
|
.display_scores(plays, ctx, msg.guild_id, (reply, ctx))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ use chrono::DateTime;
|
||||||
use pagination::paginate_with_first_message;
|
use pagination::paginate_with_first_message;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
all::{GuildId, Member, PartialGuild},
|
all::{GuildId, Member, PartialGuild},
|
||||||
builder::EditMessage,
|
|
||||||
framework::standard::{macros::command, Args, CommandResult},
|
framework::standard::{macros::command, Args, CommandResult},
|
||||||
model::channel::Message,
|
model::channel::Message,
|
||||||
utils::MessageBuilder,
|
utils::MessageBuilder,
|
||||||
|
@ -236,7 +235,7 @@ where
|
||||||
let total_len = users.len();
|
let total_len = users.len();
|
||||||
let total_pages = (total_len + ITEMS_PER_PAGE - 1) / ITEMS_PER_PAGE;
|
let total_pages = (total_len + ITEMS_PER_PAGE - 1) / ITEMS_PER_PAGE;
|
||||||
paginate_with_first_message(
|
paginate_with_first_message(
|
||||||
paginate_from_fn(move |page: u8, _: &Context, _: &Message, btns| {
|
paginate_from_fn(move |page: u8, btns| {
|
||||||
let header = header.clone();
|
let header = header.clone();
|
||||||
use Align::*;
|
use Align::*;
|
||||||
let users = users.clone();
|
let users = users.clone();
|
||||||
|
@ -317,12 +316,14 @@ where
|
||||||
last_update.format("<t:%s:R>"),
|
last_update.format("<t:%s:R>"),
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
Ok(Some(EditMessage::new().content(content).components(btns)))
|
Ok(Some(
|
||||||
|
CreateReply::default().content(content).components(btns),
|
||||||
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.with_page_count(total_pages),
|
.with_page_count(total_pages),
|
||||||
ctx,
|
ctx,
|
||||||
msg,
|
(msg, ctx),
|
||||||
std::time::Duration::from_secs(60),
|
std::time::Duration::from_secs(60),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -425,7 +426,7 @@ pub async fn show_leaderboard(ctx: &Context, msg: &Message, mut args: Args) -> C
|
||||||
scores.into_iter().map(|s| s.score).collect(),
|
scores.into_iter().map(|s| s.score).collect(),
|
||||||
ctx,
|
ctx,
|
||||||
Some(guild),
|
Some(guild),
|
||||||
reply,
|
(reply, ctx),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
@ -614,7 +615,7 @@ pub async fn display_rankings_table(
|
||||||
let header = Arc::new(to.content.clone());
|
let header = Arc::new(to.content.clone());
|
||||||
|
|
||||||
paginate_with_first_message(
|
paginate_with_first_message(
|
||||||
paginate_from_fn(move |page: u8, _, _, btns| {
|
paginate_from_fn(move |page: u8, btns| {
|
||||||
let start = (page as usize) * ITEMS_PER_PAGE;
|
let start = (page as usize) * ITEMS_PER_PAGE;
|
||||||
let end = (start + ITEMS_PER_PAGE).min(scores.len());
|
let end = (start + ITEMS_PER_PAGE).min(scores.len());
|
||||||
if start >= end {
|
if start >= end {
|
||||||
|
@ -717,12 +718,14 @@ pub async fn display_rankings_table(
|
||||||
))
|
))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Ok(Some(EditMessage::new().content(content).components(btns)))
|
Ok(Some(
|
||||||
|
CreateReply::default().content(content).components(btns),
|
||||||
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.with_page_count(total_pages),
|
.with_page_count(total_pages),
|
||||||
ctx,
|
ctx,
|
||||||
to,
|
(to, ctx),
|
||||||
std::time::Duration::from_secs(60),
|
std::time::Duration::from_secs(60),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -18,7 +18,8 @@ pub use debugging_ok::OkPrint;
|
||||||
pub use flags::Flags;
|
pub use flags::Flags;
|
||||||
pub use hook::Hook;
|
pub use hook::Hook;
|
||||||
pub use member_cache::MemberCache;
|
pub use member_cache::MemberCache;
|
||||||
pub use pagination::{paginate, paginate_from_fn, paginate_reply, Paginate};
|
pub use pagination::{paginate, paginate_from_fn, paginate_reply, CanEdit, Paginate};
|
||||||
|
pub use poise::CreateReply;
|
||||||
|
|
||||||
pub mod announcer;
|
pub mod announcer;
|
||||||
pub mod args;
|
pub mod args;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::{Context, OkPrint, Result};
|
use crate::{CmdContext, Context, OkPrint, Result};
|
||||||
use futures_util::future::Future;
|
use futures_util::future::Future;
|
||||||
|
use poise::{CreateReply, ReplyHandle};
|
||||||
use serenity::{
|
use serenity::{
|
||||||
all::{
|
all::{
|
||||||
CreateActionRow, CreateButton, CreateInteractionResponse, EditMessage, Interaction,
|
ComponentInteraction, CreateActionRow, CreateButton, CreateInteractionResponse,
|
||||||
MessageId,
|
EditInteractionResponse, EditMessage, Interaction, MessageId,
|
||||||
},
|
},
|
||||||
builder::CreateMessage,
|
builder::CreateMessage,
|
||||||
model::{
|
model::{
|
||||||
|
@ -15,28 +16,68 @@ use serenity::{
|
||||||
use std::{convert::TryFrom, sync::Arc};
|
use std::{convert::TryFrom, sync::Arc};
|
||||||
use tokio::time as tokio_time;
|
use tokio::time as tokio_time;
|
||||||
|
|
||||||
const ARROW_RIGHT: &str = "➡️";
|
// const ARROW_RIGHT: &str = "➡️";
|
||||||
const ARROW_LEFT: &str = "⬅️";
|
// const ARROW_LEFT: &str = "⬅️";
|
||||||
const REWIND: &str = "⏪";
|
// const REWIND: &str = "⏪";
|
||||||
const FAST_FORWARD: &str = "⏩";
|
// const FAST_FORWARD: &str = "⏩";
|
||||||
|
|
||||||
const NEXT: &str = "youmubot_pagination_next";
|
const NEXT: &str = "youmubot_pagination_next";
|
||||||
const PREV: &str = "youmubot_pagination_prev";
|
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 {
|
||||||
|
fn get_message(&self) -> impl Future<Output = Result<Message>> + Send;
|
||||||
|
fn apply_edit(&mut self, edit: CreateReply) -> impl Future<Output = Result<()>> + Send;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CanEdit for (Message, &'a Context) {
|
||||||
|
async fn get_message(&self) -> Result<Message> {
|
||||||
|
Ok(self.0.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn apply_edit(&mut self, edit: CreateReply) -> Result<()> {
|
||||||
|
self.0
|
||||||
|
.edit(&self.1, edit.to_prefix_edit(EditMessage::new()))
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> CanEdit for (&'a ComponentInteraction, &'b Context) {
|
||||||
|
async fn get_message(&self) -> Result<Message> {
|
||||||
|
Ok(self.0.get_response(&self.1.http).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn apply_edit(&mut self, edit: CreateReply) -> Result<()> {
|
||||||
|
self.0
|
||||||
|
.edit_response(
|
||||||
|
&self.1,
|
||||||
|
edit.to_slash_initial_response_edit(EditInteractionResponse::new()),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'e, Env: Send + Sync> CanEdit for (ReplyHandle<'a>, CmdContext<'e, Env>) {
|
||||||
|
async fn get_message(&self) -> Result<Message> {
|
||||||
|
Ok(self.0.message().await?.into_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn apply_edit(&mut self, edit: CreateReply) -> Result<()> {
|
||||||
|
self.0.edit(self.1, edit).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A trait that provides the implementation of a paginator.
|
/// A trait that provides the implementation of a paginator.
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait Paginate: Send + Sized {
|
pub trait Paginate: Send + Sized {
|
||||||
/// Render the given page.
|
/// Render the given page.
|
||||||
/// Remember to add the [[interaction_buttons]] as an action row!
|
/// Remember to add the [[interaction_buttons]] as an action row!
|
||||||
async fn render(
|
async fn render(&mut self, page: u8, btns: Vec<CreateActionRow>)
|
||||||
&mut self,
|
-> Result<Option<CreateReply>>;
|
||||||
page: u8,
|
|
||||||
ctx: &Context,
|
|
||||||
m: &Message,
|
|
||||||
btns: Vec<CreateActionRow>,
|
|
||||||
) -> Result<Option<EditMessage>>;
|
|
||||||
|
|
||||||
// /// The [[CreateActionRow]] for pagination.
|
// /// The [[CreateActionRow]] for pagination.
|
||||||
// fn pagination_row(&self) -> CreateActionRow {
|
// fn pagination_row(&self) -> CreateActionRow {
|
||||||
|
@ -54,11 +95,11 @@ pub trait Paginate: Send + Sized {
|
||||||
async fn handle_reaction(
|
async fn handle_reaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
page: u8,
|
page: u8,
|
||||||
ctx: &Context,
|
_ctx: &Context,
|
||||||
message: &mut Message,
|
message: &mut impl CanEdit,
|
||||||
reaction: &str,
|
reaction: &str,
|
||||||
) -> Result<Option<u8>> {
|
) -> Result<Option<u8>> {
|
||||||
handle_pagination_reaction(page, self, ctx, message, reaction)
|
handle_pagination_reaction(page, self, message, reaction)
|
||||||
.await
|
.await
|
||||||
.map(Some)
|
.map(Some)
|
||||||
}
|
}
|
||||||
|
@ -82,25 +123,19 @@ pub trait Paginate: Send + Sized {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn do_render(
|
pub async fn do_render(p: &mut impl Paginate, page: u8, m: &mut impl CanEdit) -> Result<bool> {
|
||||||
p: &mut impl Paginate,
|
|
||||||
page: u8,
|
|
||||||
ctx: &Context,
|
|
||||||
m: &mut Message,
|
|
||||||
) -> Result<bool> {
|
|
||||||
let btns = vec![CreateActionRow::Buttons(p.interaction_buttons())];
|
let btns = vec![CreateActionRow::Buttons(p.interaction_buttons())];
|
||||||
do_render_with_btns(p, page, ctx, m, btns).await
|
do_render_with_btns(p, page, m, btns).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_render_with_btns(
|
async fn do_render_with_btns(
|
||||||
p: &mut impl Paginate,
|
p: &mut impl Paginate,
|
||||||
page: u8,
|
page: u8,
|
||||||
ctx: &Context,
|
m: &mut impl CanEdit,
|
||||||
m: &mut Message,
|
|
||||||
btns: Vec<CreateActionRow>,
|
btns: Vec<CreateActionRow>,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
if let Some(edit) = p.render(page, ctx, m, btns).await? {
|
if let Some(edit) = p.render(page, btns).await? {
|
||||||
m.edit(ctx, edit).await?;
|
m.apply_edit(edit).await?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
|
@ -108,29 +143,24 @@ async fn do_render_with_btns(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paginate_from_fn(
|
pub fn paginate_from_fn(
|
||||||
pager: impl for<'m> FnMut(
|
pager: impl FnMut(
|
||||||
u8,
|
u8,
|
||||||
&'m Context,
|
|
||||||
&'m Message,
|
|
||||||
Vec<CreateActionRow>,
|
Vec<CreateActionRow>,
|
||||||
) -> std::pin::Pin<
|
)
|
||||||
Box<dyn Future<Output = Result<Option<EditMessage>>> + Send + 'm>,
|
-> std::pin::Pin<Box<dyn Future<Output = Result<Option<CreateReply>>> + Send>>
|
||||||
> + Send,
|
+ Send,
|
||||||
) -> impl Paginate {
|
) -> impl Paginate {
|
||||||
pager
|
pager
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_buttons(p: &impl Paginate) -> Vec<CreateButton> {
|
pub fn default_buttons(p: &impl Paginate) -> Vec<CreateButton> {
|
||||||
let mut btns = vec![
|
let mut btns = vec![
|
||||||
CreateButton::new(PREV).emoji(ReactionType::try_from(ARROW_LEFT).unwrap()),
|
CreateButton::new(PREV).label("<"),
|
||||||
CreateButton::new(NEXT).emoji(ReactionType::try_from(ARROW_RIGHT).unwrap()),
|
CreateButton::new(NEXT).label(">"),
|
||||||
];
|
];
|
||||||
if p.len().is_some_and(|v| v > 5) {
|
if p.len().is_some_and(|v| v > 5) {
|
||||||
btns.insert(
|
btns.insert(0, CreateButton::new(FAST_PREV).label("<<"));
|
||||||
0,
|
btns.push(CreateButton::new(FAST_NEXT).label(">>"))
|
||||||
CreateButton::new(FAST_PREV).emoji(ReactionType::try_from(REWIND).unwrap()),
|
|
||||||
);
|
|
||||||
btns.push(CreateButton::new(FAST_NEXT).emoji(ReactionType::try_from(FAST_FORWARD).unwrap()))
|
|
||||||
}
|
}
|
||||||
btns
|
btns
|
||||||
}
|
}
|
||||||
|
@ -145,21 +175,19 @@ impl<Inner: Paginate> Paginate for WithPageCount<Inner> {
|
||||||
async fn render(
|
async fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
page: u8,
|
page: u8,
|
||||||
ctx: &Context,
|
|
||||||
m: &Message,
|
|
||||||
btns: Vec<CreateActionRow>,
|
btns: Vec<CreateActionRow>,
|
||||||
) -> Result<Option<EditMessage>> {
|
) -> Result<Option<CreateReply>> {
|
||||||
if page as usize >= self.page_count {
|
if page as usize >= self.page_count {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
self.inner.render(page, ctx, m, btns).await
|
self.inner.render(page, btns).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_reaction(
|
async fn handle_reaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
page: u8,
|
page: u8,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
message: &mut Message,
|
message: &mut impl CanEdit,
|
||||||
reaction: &str,
|
reaction: &str,
|
||||||
) -> Result<Option<u8>> {
|
) -> Result<Option<u8>> {
|
||||||
self.inner
|
self.inner
|
||||||
|
@ -179,23 +207,19 @@ impl<Inner: Paginate> Paginate for WithPageCount<Inner> {
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<T> Paginate for T
|
impl<T> Paginate for T
|
||||||
where
|
where
|
||||||
T: for<'m> FnMut(
|
T: FnMut(
|
||||||
u8,
|
u8,
|
||||||
&'m Context,
|
|
||||||
&'m Message,
|
|
||||||
Vec<CreateActionRow>,
|
Vec<CreateActionRow>,
|
||||||
) -> std::pin::Pin<
|
)
|
||||||
Box<dyn Future<Output = Result<Option<EditMessage>>> + Send + 'm>,
|
-> std::pin::Pin<Box<dyn Future<Output = Result<Option<CreateReply>>> + Send>>
|
||||||
> + Send,
|
+ Send,
|
||||||
{
|
{
|
||||||
async fn render(
|
async fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
page: u8,
|
page: u8,
|
||||||
ctx: &Context,
|
|
||||||
m: &Message,
|
|
||||||
btns: Vec<CreateActionRow>,
|
btns: Vec<CreateActionRow>,
|
||||||
) -> Result<Option<EditMessage>> {
|
) -> Result<Option<CreateReply>> {
|
||||||
self(page, ctx, m, btns).await
|
self(page, btns).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +234,7 @@ pub async fn paginate_reply(
|
||||||
let message = reply_to
|
let message = reply_to
|
||||||
.reply(&ctx, "Youmu is loading the first page...")
|
.reply(&ctx, "Youmu is loading the first page...")
|
||||||
.await?;
|
.await?;
|
||||||
|
let message = (message, ctx);
|
||||||
paginate_with_first_message(pager, ctx, message, timeout).await
|
paginate_with_first_message(pager, ctx, message, timeout).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +252,7 @@ pub async fn paginate(
|
||||||
CreateMessage::new().content("Youmu is loading the first page..."),
|
CreateMessage::new().content("Youmu is loading the first page..."),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
let message = (message, ctx);
|
||||||
paginate_with_first_message(pager, ctx, message, timeout).await
|
paginate_with_first_message(pager, ctx, message, timeout).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,13 +260,14 @@ pub async fn paginate(
|
||||||
pub async fn paginate_with_first_message(
|
pub async fn paginate_with_first_message(
|
||||||
mut pager: impl Paginate,
|
mut pager: impl Paginate,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
mut message: Message,
|
mut message: impl CanEdit,
|
||||||
timeout: std::time::Duration,
|
timeout: std::time::Duration,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let msg_id = message.get_message().await?.id;
|
||||||
let (send, recv) = flume::unbounded::<String>();
|
let (send, recv) = flume::unbounded::<String>();
|
||||||
Paginator::push(ctx, &message, send).await?;
|
Paginator::push(ctx, msg_id, send).await?;
|
||||||
|
|
||||||
do_render(&mut pager, 0, ctx, &mut message).await?;
|
do_render(&mut pager, 0, &mut message).await?;
|
||||||
// Just quit if there is only one page
|
// Just quit if there is only one page
|
||||||
if pager.len().filter(|&v| v == 1).is_some() {
|
if pager.len().filter(|&v| v == 1).is_some() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -266,10 +293,10 @@ pub async fn paginate_with_first_message(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render one last time with no buttons
|
// Render one last time with no buttons
|
||||||
do_render_with_btns(&mut pager, page, ctx, &mut message, vec![])
|
do_render_with_btns(&mut pager, page, &mut message, vec![])
|
||||||
.await
|
.await
|
||||||
.pls_ok();
|
.pls_ok();
|
||||||
Paginator::pop(ctx, &message).await?;
|
Paginator::pop(ctx, msg_id).await?;
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
@ -278,8 +305,7 @@ pub async fn paginate_with_first_message(
|
||||||
pub async fn handle_pagination_reaction(
|
pub async fn handle_pagination_reaction(
|
||||||
page: u8,
|
page: u8,
|
||||||
pager: &mut impl Paginate,
|
pager: &mut impl Paginate,
|
||||||
ctx: &Context,
|
message: &mut impl CanEdit,
|
||||||
message: &mut Message,
|
|
||||||
reaction: &str,
|
reaction: &str,
|
||||||
) -> Result<u8> {
|
) -> Result<u8> {
|
||||||
let pages = pager.len();
|
let pages = pager.len();
|
||||||
|
@ -299,7 +325,7 @@ pub async fn handle_pagination_reaction(
|
||||||
FAST_NEXT => (pages.unwrap() as u8 - 1).min(page + fast),
|
FAST_NEXT => (pages.unwrap() as u8 - 1).min(page + fast),
|
||||||
_ => return Ok(page),
|
_ => return Ok(page),
|
||||||
};
|
};
|
||||||
Ok(if do_render(pager, new_page, ctx, message).await? {
|
Ok(if do_render(pager, new_page, message).await? {
|
||||||
new_page
|
new_page
|
||||||
} else {
|
} else {
|
||||||
page
|
page
|
||||||
|
@ -318,25 +344,25 @@ impl Paginator {
|
||||||
channels: Arc::new(dashmap::DashMap::new()),
|
channels: Arc::new(dashmap::DashMap::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn push(ctx: &Context, msg: &Message, channel: flume::Sender<String>) -> Result<()> {
|
async fn push(ctx: &Context, msg: MessageId, channel: flume::Sender<String>) -> Result<()> {
|
||||||
ctx.data
|
ctx.data
|
||||||
.write()
|
.read()
|
||||||
.await
|
.await
|
||||||
.get_mut::<Paginator>()
|
.get::<Paginator>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.channels
|
.channels
|
||||||
.insert(msg.id, channel);
|
.insert(msg, channel);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn pop(ctx: &Context, msg: &Message) -> Result<()> {
|
async fn pop(ctx: &Context, msg: MessageId) -> Result<()> {
|
||||||
ctx.data
|
ctx.data
|
||||||
.write()
|
.read()
|
||||||
.await
|
.await
|
||||||
.get_mut::<Paginator>()
|
.get::<Paginator>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.channels
|
.channels
|
||||||
.remove(&msg.id);
|
.remove(&msg);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue