diff --git a/youmubot-prelude/src/editable.rs b/youmubot-prelude/src/editable.rs new file mode 100644 index 0000000..2197b24 --- /dev/null +++ b/youmubot-prelude/src/editable.rs @@ -0,0 +1,50 @@ +use poise::{CreateReply, ReplyHandle}; +use serenity::all::Message; + +use crate::CmdContext; +use core::future::Future; + +/// Represents an editable message context. +pub trait Editable { + /// Edits the underlying message. + fn edit_msg(&mut self, reply: CreateReply) -> impl Future> + Send; +} + +struct ReplyHandleEdit<'a, 'env, Env>(ReplyHandle<'a>, CmdContext<'env, Env>); + +impl<'a, 'b, Env: Send + Sync> Editable for ReplyHandleEdit<'a, 'b, Env> { + async fn edit_msg(&mut self, reply: CreateReply) -> anyhow::Result<()> { + Ok(self.0.edit(self.1.clone(), reply).await?) + } +} + +/// Returns an [`Editable`] from a [`ReplyHandle`]. +pub fn editable_reply_handle<'a, 'b, Env: Send + Sync>( + reply: ReplyHandle<'a>, + ctx: CmdContext<'b, Env>, +) -> impl Editable + use<'a, 'b, Env> { + ReplyHandleEdit(reply, ctx) +} + +struct MsgEdit(Message, serenity::all::Context); + +/// Returns an [`Editable`] from a [`Message`]. +pub fn editable_message(msg: Message, ctx: serenity::all::Context) -> impl Editable { + MsgEdit(msg, ctx) +} + +impl Editable for MsgEdit { + async fn edit_msg(&mut self, reply: CreateReply) -> anyhow::Result<()> { + self.0 + .edit(&self.1, { + // Clear builder so that adding embeds or attachments won't add on top of + // the pre-edit items but replace them (which is apparently the more + // intuitive behavior). Notably, setting the builder to default doesn't + // mean the entire message is reset to empty: Discord only updates parts + // of the message that have had a modification specified + reply.to_prefix_edit(serenity::all::EditMessage::new()) + }) + .await?; + Ok(()) + } +} diff --git a/youmubot-prelude/src/lib.rs b/youmubot-prelude/src/lib.rs index 76f03bf..72f28b7 100644 --- a/youmubot-prelude/src/lib.rs +++ b/youmubot-prelude/src/lib.rs @@ -15,6 +15,7 @@ pub use tokio::spawn as spawn_future; pub use announcer::{Announcer, AnnouncerRunner}; pub use args::{ChannelId, Duration, RoleId, UserId, UsernameArg}; pub use debugging_ok::OkPrint; +pub use editable::{editable_message, editable_reply_handle, Editable}; pub use flags::Flags; pub use hook::Hook; pub use member_cache::MemberCache; @@ -22,6 +23,7 @@ pub use pagination::{paginate, paginate_from_fn, paginate_reply, Paginate}; pub mod announcer; pub mod args; +pub mod editable; pub mod flags; pub mod hook; pub mod member_cache;