Skip to main content

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