sparkle_convenience/
message.rs

1#![allow(deprecated)]
2
3use std::fmt::{Debug, Display};
4
5use anyhow;
6use async_trait::async_trait;
7use twilight_http::{request::channel::message::CreateMessage, Response};
8use twilight_model::{
9    channel::Message,
10    id::{
11        marker::{ChannelMarker, UserMarker},
12        Id,
13    },
14};
15use twilight_validate::message::MessageValidationError;
16
17use crate::{
18    error,
19    error::{extract::HttpErrorExt, Error, ErrorExt, NoCustomError, UserError},
20    reply::Reply,
21    Bot,
22};
23
24impl Bot {
25    /// Handle an error returned in a message
26    ///
27    /// The passed reply should be the reply that should be shown to the user
28    /// based on the error
29    ///
30    /// The type parameter `Custom` is used to determine if the error is
31    /// internal, if you don't have a custom error type, you can use
32    /// [`Bot::handle_error_no_custom`]
33    ///
34    /// - If the given error should be ignored, simply returns early
35    /// - If the given error is internal, logs the error
36    /// - Tries to send the given reply to the channel, if it fails and the
37    ///   error is internal, logs the error, if it succeeds, returns the
38    ///   response
39    pub async fn handle_error<Custom: Display + Debug + Send + Sync + 'static>(
40        &self,
41        channel_id: Id<ChannelMarker>,
42        reply: Reply,
43        error: anyhow::Error,
44    ) -> Option<Response<Message>> {
45        if error.ignore() {
46            return None;
47        }
48
49        if let Some(internal_err) = error.internal::<Custom>() {
50            self.log(internal_err).await;
51        }
52
53        let create_res = match self.http.create_message(channel_id).with_reply(&reply) {
54            Ok(create) => create.await.map_err(anyhow::Error::new),
55            Err(validation_err) => Err(validation_err.into()),
56        };
57
58        match create_res.map_err(error::ErrorExt::internal::<Custom>) {
59            Ok(response) => Some(response),
60            Err(create_err) => {
61                if let Some(create_internal_err) = create_err {
62                    self.log(create_internal_err).await;
63                }
64                None
65            }
66        }
67    }
68
69    /// Handle an error without checking for a custom error type
70    ///
71    /// See [`Bot::handle_error`] for more information
72    pub async fn handle_error_no_custom(
73        &self,
74        channel_id: Id<ChannelMarker>,
75        reply: Reply,
76        error: anyhow::Error,
77    ) -> Option<Response<Message>> {
78        self.handle_error::<NoCustomError>(channel_id, reply, error)
79            .await
80    }
81}
82
83/// Convenience methods for [`twilight_http::Client`]
84#[deprecated(note = "Use `Reply::create_private_message` instead")]
85#[async_trait]
86#[allow(clippy::module_name_repetitions)]
87pub trait HttpExt {
88    /// Send a private message to a user
89    async fn dm_user(&self, user_id: Id<UserMarker>) -> Result<CreateMessage<'_>, Error>;
90}
91
92#[async_trait]
93impl HttpExt for twilight_http::Client {
94    async fn dm_user(&self, user_id: Id<UserMarker>) -> Result<CreateMessage<'_>, Error> {
95        let channel_id = self
96            .create_private_channel(user_id)
97            .await?
98            .model()
99            .await?
100            .id;
101
102        Ok(self.create_message(channel_id))
103    }
104}
105
106/// Convenience methods for [`CreateMessage`]
107#[deprecated(note = "Use `Reply::create_message` instead")]
108#[async_trait]
109pub trait CreateMessageExt<'a>: Sized {
110    /// Add the given reply's data to the message
111    ///
112    /// Overwrites previous fields
113    ///
114    /// # Errors
115    ///
116    /// Returns [`MessageValidationError`] if the
117    /// reply is invalid
118    fn with_reply(self, reply: &'a Reply) -> Result<Self, MessageValidationError>;
119
120    /// Send the message, ignoring the error if it's
121    /// [`HttpErrorExt::missing_permissions`]
122    ///
123    /// Useful when trying to report an error by sending a message
124    ///
125    /// # Errors
126    ///
127    /// Returns [`Error::Http`] if creating the response fails and the error is
128    /// not [`HttpErrorExt::missing_permissions`]
129    ///
130    /// Returns [`Error::User`] with [`UserError::Ignore`] if the error is
131    /// [`HttpErrorExt::missing_permissions`]
132    async fn execute_ignore_permissions(self) -> Result<Response<Message>, Error>;
133}
134
135#[async_trait]
136impl<'a> CreateMessageExt<'a> for CreateMessage<'a> {
137    fn with_reply(self, reply: &'a Reply) -> Result<Self, MessageValidationError> {
138        let mut message = self
139            .embeds(&reply.embeds)?
140            .components(&reply.components)?
141            .attachments(&reply.attachments)?
142            .flags(reply.flags)
143            .tts(reply.tts);
144
145        if !reply.content.is_empty() {
146            message = message.content(&reply.content)?;
147        }
148
149        if let Some(allowed_mentions) = &reply.allowed_mentions {
150            message = message.allowed_mentions(allowed_mentions.as_ref());
151        }
152
153        Ok(message)
154    }
155
156    async fn execute_ignore_permissions(self) -> Result<Response<Message>, Error> {
157        self.await.map_err(|http_err| {
158            if http_err.missing_permissions() {
159                UserError::Ignore.into()
160            } else {
161                http_err.into()
162            }
163        })
164    }
165}