serenity/builder/
edit_webhook_message.rs

1#[cfg(feature = "http")]
2use super::{check_overflow, Builder};
3use super::{
4    CreateActionRow,
5    CreateAllowedMentions,
6    CreateAttachment,
7    CreateEmbed,
8    EditAttachments,
9};
10#[cfg(feature = "http")]
11use crate::constants;
12#[cfg(feature = "http")]
13use crate::http::CacheHttp;
14#[cfg(feature = "http")]
15use crate::internal::prelude::*;
16use crate::model::prelude::*;
17
18/// A builder to specify the fields to edit in an existing [`Webhook`]'s message.
19///
20/// [Discord docs](https://discord.com/developers/docs/resources/webhook#edit-webhook-message)
21#[derive(Clone, Debug, Default, Serialize)]
22#[must_use]
23pub struct EditWebhookMessage {
24    #[serde(skip_serializing_if = "Option::is_none")]
25    content: Option<String>,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    embeds: Option<Vec<CreateEmbed>>,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    allowed_mentions: Option<CreateAllowedMentions>,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub(crate) components: Option<Vec<CreateActionRow>>,
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub(crate) attachments: Option<EditAttachments>,
34
35    #[serde(skip)]
36    thread_id: Option<ChannelId>,
37}
38
39impl EditWebhookMessage {
40    /// Equivalent to [`Self::default`].
41    pub fn new() -> Self {
42        Self::default()
43    }
44
45    #[cfg(feature = "http")]
46    pub(crate) fn check_length(&self) -> Result<()> {
47        if let Some(content) = &self.content {
48            check_overflow(content.chars().count(), constants::MESSAGE_CODE_LIMIT)
49                .map_err(|overflow| Error::Model(ModelError::MessageTooLong(overflow)))?;
50        }
51
52        if let Some(embeds) = &self.embeds {
53            check_overflow(embeds.len(), constants::EMBED_MAX_COUNT)
54                .map_err(|_| Error::Model(ModelError::EmbedAmount))?;
55            for embed in embeds {
56                embed.check_length()?;
57            }
58        }
59
60        Ok(())
61    }
62
63    /// Set the content of the message.
64    ///
65    /// **Note**: Message contents must be under 2000 unicode code points.
66    #[inline]
67    pub fn content(mut self, content: impl Into<String>) -> Self {
68        self.content = Some(content.into());
69        self
70    }
71
72    /// Edits a message within a given thread. If the provided thread Id doesn't belong to the
73    /// current webhook, the API will return an error.
74    #[inline]
75    pub fn in_thread(mut self, thread_id: impl Into<ChannelId>) -> Self {
76        self.thread_id = Some(thread_id.into());
77        self
78    }
79
80    /// Adds an embed for the message.
81    ///
82    /// Embeds from the original message are reset when adding new embeds and must be re-added.
83    pub fn add_embed(mut self, embed: CreateEmbed) -> Self {
84        self.embeds.get_or_insert(Vec::new()).push(embed);
85        self
86    }
87
88    /// Adds multiple embeds to the message.
89    ///
90    /// Embeds from the original message are reset when adding new embeds and must be re-added.
91    pub fn add_embeds(mut self, embeds: Vec<CreateEmbed>) -> Self {
92        self.embeds.get_or_insert(Vec::new()).extend(embeds);
93        self
94    }
95
96    /// Sets a single embed to include in the message
97    ///
98    /// Calling this will overwrite the embed list. To append embeds, call [`Self::add_embed`]
99    /// instead.
100    pub fn embed(mut self, embed: CreateEmbed) -> Self {
101        self.embeds = Some(vec![embed]);
102        self
103    }
104
105    /// Sets the embeds for the message.
106    ///
107    /// **Note**: You can only have up to 10 embeds per message.
108    ///
109    /// Calling this will overwrite the embed list. To append embeds, call [`Self::add_embeds`]
110    /// instead.
111    pub fn embeds(mut self, embeds: Vec<CreateEmbed>) -> Self {
112        self.embeds = Some(embeds);
113        self
114    }
115
116    /// Set the allowed mentions for the message.
117    pub fn allowed_mentions(mut self, allowed_mentions: CreateAllowedMentions) -> Self {
118        self.allowed_mentions = Some(allowed_mentions);
119        self
120    }
121
122    /// Sets the components for this message. Requires an application-owned webhook, meaning either
123    /// the webhook's `kind` field is set to [`WebhookType::Application`], or it was created by an
124    /// application (and has kind [`WebhookType::Incoming`]).
125    ///
126    /// [`WebhookType::Application`]: crate::model::webhook::WebhookType
127    /// [`WebhookType::Incoming`]: crate::model::webhook::WebhookType
128    pub fn components(mut self, components: Vec<CreateActionRow>) -> Self {
129        self.components = Some(components);
130        self
131    }
132    super::button_and_select_menu_convenience_methods!(self.components);
133
134    /// Sets attachments, see [`EditAttachments`] for more details.
135    pub fn attachments(mut self, attachments: EditAttachments) -> Self {
136        self.attachments = Some(attachments);
137        self
138    }
139
140    /// Adds a new attachment to the message.
141    ///
142    /// Resets existing attachments. See the documentation for [`EditAttachments`] for details.
143    pub fn new_attachment(mut self, attachment: CreateAttachment) -> Self {
144        let attachments = self.attachments.get_or_insert_with(Default::default);
145        self.attachments = Some(std::mem::take(attachments).add(attachment));
146        self
147    }
148
149    /// Shorthand for [`EditAttachments::keep`].
150    pub fn keep_existing_attachment(mut self, id: AttachmentId) -> Self {
151        let attachments = self.attachments.get_or_insert_with(Default::default);
152        self.attachments = Some(std::mem::take(attachments).keep(id));
153        self
154    }
155
156    /// Shorthand for calling [`Self::attachments`] with [`EditAttachments::new`].
157    pub fn clear_attachments(mut self) -> Self {
158        self.attachments = Some(EditAttachments::new());
159        self
160    }
161}
162
163#[cfg(feature = "http")]
164#[async_trait::async_trait]
165impl Builder for EditWebhookMessage {
166    type Context<'ctx> = (WebhookId, &'ctx str, MessageId);
167    type Built = Message;
168
169    /// Edits the webhook's message.
170    ///
171    /// **Note**: Message contents must be under 2000 unicode code points, and embeds must be under
172    /// 6000 code points.
173    ///
174    /// # Errors
175    ///
176    /// Returns an [`Error::Model`] if the message content is too long.
177    ///
178    /// May also return an [`Error::Http`] if the content is malformed, the webhook's token is
179    /// invalid, or the given message Id does not belong to the webhook.
180    ///
181    /// Or may return an [`Error::Json`] if there is an error deserialising Discord's response.
182    async fn execute(
183        mut self,
184        cache_http: impl CacheHttp,
185        ctx: Self::Context<'_>,
186    ) -> Result<Self::Built> {
187        self.check_length()?;
188
189        let files = self.attachments.as_mut().map_or(Vec::new(), |a| a.take_files());
190
191        let http = cache_http.http();
192        if self.allowed_mentions.is_none() {
193            self.allowed_mentions.clone_from(&http.default_allowed_mentions);
194        }
195
196        http.edit_webhook_message(ctx.0, self.thread_id, ctx.1, ctx.2, &self, files).await
197    }
198}