twilight_http/request/channel/message/
create_message.rs1use crate::{
2 client::Client,
3 error::Error,
4 request::{
5 Nullable, Request, TryIntoRequest,
6 attachment::{AttachmentManager, PartialAttachment},
7 },
8 response::{Response, ResponseFuture},
9 routing::Route,
10};
11use serde::Serialize;
12use std::future::IntoFuture;
13use twilight_model::{
14 channel::message::{
15 AllowedMentions, Component, Embed, Message, MessageFlags, MessageReference,
16 MessageReferenceType,
17 },
18 http::attachment::Attachment,
19 id::{
20 Id,
21 marker::{ChannelMarker, MessageMarker, StickerMarker},
22 },
23 poll::Poll,
24};
25use twilight_validate::message::{
26 MessageValidationError, attachment as validate_attachment, components as validate_components,
27 content as validate_content, embeds as validate_embeds, sticker_ids as validate_sticker_ids,
28};
29
30#[derive(Serialize)]
31pub(crate) struct CreateMessageFields<'a> {
32 #[serde(skip_serializing_if = "Option::is_none")]
33 allowed_mentions: Option<Nullable<&'a AllowedMentions>>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 attachments: Option<Vec<PartialAttachment<'a>>>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 components: Option<&'a [Component]>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 content: Option<&'a str>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 embeds: Option<&'a [Embed]>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 flags: Option<MessageFlags>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 message_reference: Option<MessageReference>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 nonce: Option<u64>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 payload_json: Option<&'a [u8]>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 poll: Option<&'a Poll>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 sticker_ids: Option<&'a [Id<StickerMarker>]>,
54 #[serde(skip_serializing_if = "Option::is_none")]
55 tts: Option<bool>,
56}
57
58#[must_use = "requests must be configured and executed"]
87pub struct CreateMessage<'a> {
88 attachment_manager: AttachmentManager<'a>,
89 channel_id: Id<ChannelMarker>,
90 fields: Result<CreateMessageFields<'a>, MessageValidationError>,
91 http: &'a Client,
92}
93
94impl<'a> CreateMessage<'a> {
95 pub(crate) const fn new(http: &'a Client, channel_id: Id<ChannelMarker>) -> Self {
96 Self {
97 attachment_manager: AttachmentManager::new(),
98 channel_id,
99 fields: Ok(CreateMessageFields {
100 attachments: None,
101 components: None,
102 content: None,
103 embeds: None,
104 flags: None,
105 message_reference: None,
106 nonce: None,
107 payload_json: None,
108 poll: None,
109 allowed_mentions: None,
110 sticker_ids: None,
111 tts: None,
112 }),
113 http,
114 }
115 }
116
117 pub const fn allowed_mentions(mut self, allowed_mentions: Option<&'a AllowedMentions>) -> Self {
122 if let Ok(fields) = self.fields.as_mut() {
123 fields.allowed_mentions = Some(Nullable(allowed_mentions));
124 }
125
126 self
127 }
128
129 pub fn attachments(mut self, attachments: &'a [Attachment]) -> Self {
144 if self.fields.is_ok() {
145 if let Err(source) = attachments.iter().try_for_each(validate_attachment) {
146 self.fields = Err(source);
147 } else {
148 self.attachment_manager = self
149 .attachment_manager
150 .set_files(attachments.iter().collect());
151 }
152 }
153
154 self
155 }
156
157 pub fn components(mut self, components: &'a [Component]) -> Self {
167 self.fields = self.fields.and_then(|mut fields| {
168 validate_components(
169 components,
170 fields
171 .flags
172 .is_some_and(|flags| flags.contains(MessageFlags::IS_COMPONENTS_V2)),
173 )?;
174 fields.components = Some(components);
175
176 Ok(fields)
177 });
178
179 self
180 }
181
182 pub fn content(mut self, content: &'a str) -> Self {
193 self.fields = self.fields.and_then(|mut fields| {
194 validate_content(content)?;
195 fields.content.replace(content);
196
197 Ok(fields)
198 });
199
200 self
201 }
202
203 pub fn embeds(mut self, embeds: &'a [Embed]) -> Self {
224 self.fields = self.fields.and_then(|mut fields| {
225 validate_embeds(embeds)?;
226 fields.embeds = Some(embeds);
227
228 Ok(fields)
229 });
230
231 self
232 }
233
234 pub const fn poll(mut self, poll: &'a Poll) -> Self {
236 if let Ok(fields) = self.fields.as_mut() {
237 fields.poll = Some(poll);
238 }
239
240 self
241 }
242
243 pub fn fail_if_not_exists(mut self, fail_if_not_exists: bool) -> Self {
247 if let Ok(fields) = self.fields.as_mut() {
248 if let Some(reference) = fields.message_reference.as_mut() {
249 reference.fail_if_not_exists = Some(fail_if_not_exists);
250 } else {
251 fields.message_reference = Some(MessageReference {
252 kind: MessageReferenceType::default(),
253 channel_id: None,
254 guild_id: None,
255 message_id: None,
256 fail_if_not_exists: Some(fail_if_not_exists),
257 });
258 }
259 }
260
261 self
262 }
263
264 pub const fn flags(mut self, flags: MessageFlags) -> Self {
272 if let Ok(fields) = self.fields.as_mut() {
273 fields.flags = Some(flags);
274 }
275
276 self
277 }
278
279 pub const fn nonce(mut self, nonce: u64) -> Self {
281 if let Ok(fields) = self.fields.as_mut() {
282 fields.nonce = Some(nonce);
283 }
284
285 self
286 }
287
288 pub const fn payload_json(mut self, payload_json: &'a [u8]) -> Self {
301 if let Ok(fields) = self.fields.as_mut() {
302 fields.payload_json = Some(payload_json);
303 }
304
305 self
306 }
307
308 pub fn reply(mut self, other: Id<MessageMarker>) -> Self {
310 self.fields = self.fields.map(|mut fields| {
311 let channel_id = self.channel_id;
312
313 let reference = if let Some(reference) = fields.message_reference {
314 MessageReference {
315 channel_id: Some(channel_id),
316 message_id: Some(other),
317 ..reference
318 }
319 } else {
320 MessageReference {
321 kind: MessageReferenceType::Default,
322 channel_id: Some(channel_id),
323 guild_id: None,
324 message_id: Some(other),
325 fail_if_not_exists: None,
326 }
327 };
328
329 fields.message_reference = Some(reference);
330
331 fields
332 });
333
334 self
335 }
336
337 pub fn forward(mut self, channel_id: Id<ChannelMarker>, message_id: Id<MessageMarker>) -> Self {
339 self.fields = self.fields.map(|mut fields| {
340 let reference = if let Some(reference) = fields.message_reference {
341 MessageReference {
342 channel_id: Some(channel_id),
343 message_id: Some(message_id),
344 ..reference
345 }
346 } else {
347 MessageReference {
348 kind: MessageReferenceType::Forward,
349 channel_id: Some(channel_id),
350 guild_id: None,
351 message_id: Some(message_id),
352 fail_if_not_exists: None,
353 }
354 };
355
356 fields.message_reference = Some(reference);
357
358 fields
359 });
360
361 self
362 }
363
364 pub fn sticker_ids(mut self, sticker_ids: &'a [Id<StickerMarker>]) -> Self {
372 self.fields = self.fields.and_then(|mut fields| {
373 validate_sticker_ids(sticker_ids)?;
374 fields.sticker_ids = Some(sticker_ids);
375
376 Ok(fields)
377 });
378
379 self
380 }
381
382 pub const fn tts(mut self, tts: bool) -> Self {
384 if let Ok(fields) = self.fields.as_mut() {
385 fields.tts = Some(tts);
386 }
387
388 self
389 }
390}
391
392impl IntoFuture for CreateMessage<'_> {
393 type Output = Result<Response<Message>, Error>;
394
395 type IntoFuture = ResponseFuture<Message>;
396
397 fn into_future(self) -> Self::IntoFuture {
398 let http = self.http;
399
400 match self.try_into_request() {
401 Ok(request) => http.request(request),
402 Err(source) => ResponseFuture::error(source),
403 }
404 }
405}
406
407impl TryIntoRequest for CreateMessage<'_> {
408 fn try_into_request(self) -> Result<Request, Error> {
409 let mut fields = self.fields.map_err(Error::validation)?;
410 let mut request = Request::builder(&Route::CreateMessage {
411 channel_id: self.channel_id.get(),
412 });
413
414 if fields.allowed_mentions.is_none()
416 && let Some(allowed_mentions) = self.http.default_allowed_mentions()
417 {
418 fields.allowed_mentions = Some(Nullable(Some(allowed_mentions)));
419 }
420
421 if !self.attachment_manager.is_empty() {
424 let form = if let Some(payload_json) = fields.payload_json {
425 self.attachment_manager.build_form(payload_json)
426 } else {
427 fields.attachments = Some(self.attachment_manager.get_partial_attachments());
428
429 let fields = crate::json::to_vec(&fields).map_err(Error::json)?;
430
431 self.attachment_manager.build_form(fields.as_ref())
432 };
433
434 request = request.form(form);
435 } else if let Some(payload_json) = fields.payload_json {
436 request = request.body(payload_json.to_vec());
437 } else {
438 request = request.json(&fields);
439 }
440
441 request.build()
442 }
443}