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