twilight_http/request/application/interaction/
create_followup.rs1#[cfg(not(target_os = "wasi"))]
2use crate::response::{Response, ResponseFuture};
3use crate::{
4 client::Client,
5 error::Error,
6 request::{
7 Nullable, Request, TryIntoRequest,
8 attachment::{AttachmentManager, PartialAttachment},
9 },
10 routing::Route,
11};
12use serde::Serialize;
13use std::future::IntoFuture;
14use twilight_model::{
15 channel::message::{AllowedMentions, Component, Embed, Message, MessageFlags},
16 http::attachment::Attachment,
17 id::{Id, marker::ApplicationMarker},
18 poll::Poll,
19};
20use twilight_validate::message::{
21 MessageValidationError, attachment as validate_attachment, components as validate_components,
22 content as validate_content, embeds as validate_embeds,
23};
24
25#[derive(Serialize)]
26struct CreateFollowupFields<'a> {
27 #[serde(skip_serializing_if = "Option::is_none")]
28 allowed_mentions: Option<Nullable<&'a AllowedMentions>>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 attachments: Option<Vec<PartialAttachment<'a>>>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 components: Option<&'a [Component]>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 content: Option<&'a str>,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 embeds: Option<&'a [Embed]>,
37 #[serde(skip_serializing_if = "Option::is_none")]
38 payload_json: Option<&'a [u8]>,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 tts: Option<bool>,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 flags: Option<MessageFlags>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 poll: Option<Poll>,
45}
46
47#[must_use = "requests must be configured and executed"]
78pub struct CreateFollowup<'a> {
79 application_id: Id<ApplicationMarker>,
80 attachment_manager: AttachmentManager<'a>,
81 fields: Result<CreateFollowupFields<'a>, MessageValidationError>,
82 http: &'a Client,
83 token: &'a str,
84}
85
86impl<'a> CreateFollowup<'a> {
87 pub(crate) const fn new(
88 http: &'a Client,
89 application_id: Id<ApplicationMarker>,
90 token: &'a str,
91 ) -> Self {
92 Self {
93 application_id,
94 attachment_manager: AttachmentManager::new(),
95 fields: Ok(CreateFollowupFields {
96 allowed_mentions: None,
97 attachments: None,
98 components: None,
99 content: None,
100 embeds: None,
101 payload_json: None,
102 tts: None,
103 flags: None,
104 poll: None,
105 }),
106 http,
107 token,
108 }
109 }
110
111 pub const fn allowed_mentions(mut self, allowed_mentions: Option<&'a AllowedMentions>) -> Self {
116 if let Ok(fields) = self.fields.as_mut() {
117 fields.allowed_mentions = Some(Nullable(allowed_mentions));
118 }
119
120 self
121 }
122
123 pub fn attachments(mut self, attachments: &'a [Attachment]) -> Self {
138 if self.fields.is_ok() {
139 if let Err(source) = attachments.iter().try_for_each(validate_attachment) {
140 self.fields = Err(source);
141 } else {
142 self.attachment_manager = self
143 .attachment_manager
144 .set_files(attachments.iter().collect());
145 }
146 }
147
148 self
149 }
150
151 pub fn components(mut self, components: &'a [Component]) -> Self {
161 self.fields = self.fields.and_then(|mut fields| {
162 validate_components(
163 components,
164 fields
165 .flags
166 .is_some_and(|flags| flags.contains(MessageFlags::IS_COMPONENTS_V2)),
167 )?;
168 fields.components = Some(components);
169
170 Ok(fields)
171 });
172
173 self
174 }
175
176 pub fn content(mut self, content: &'a str) -> Self {
187 self.fields = self.fields.and_then(|mut fields| {
188 validate_content(content)?;
189 fields.content = Some(content);
190
191 Ok(fields)
192 });
193
194 self
195 }
196
197 pub fn embeds(mut self, embeds: &'a [Embed]) -> Self {
218 self.fields = self.fields.and_then(|mut fields| {
219 validate_embeds(embeds)?;
220 fields.embeds = Some(embeds);
221
222 Ok(fields)
223 });
224
225 self
226 }
227
228 pub const fn flags(mut self, flags: MessageFlags) -> Self {
236 if let Ok(fields) = self.fields.as_mut() {
237 fields.flags = Some(flags);
238 }
239
240 self
241 }
242
243 pub const fn payload_json(mut self, payload_json: &'a [u8]) -> Self {
256 if let Ok(fields) = self.fields.as_mut() {
257 fields.payload_json = Some(payload_json);
258 }
259
260 self
261 }
262
263 pub const fn tts(mut self, tts: bool) -> Self {
265 if let Ok(fields) = self.fields.as_mut() {
266 fields.tts = Some(tts);
267 }
268
269 self
270 }
271
272 pub fn poll(mut self, poll: Poll) -> Self {
274 if let Ok(fields) = self.fields.as_mut() {
275 fields.poll = Some(poll);
276 }
277
278 self
279 }
280}
281
282#[cfg(not(target_os = "wasi"))]
283impl IntoFuture for CreateFollowup<'_> {
284 type Output = Result<Response<Message>, Error>;
285
286 type IntoFuture = ResponseFuture<Message>;
287
288 fn into_future(self) -> Self::IntoFuture {
289 let http = self.http;
290
291 match self.try_into_request() {
292 Ok(request) => http.request(request),
293 Err(source) => ResponseFuture::error(source),
294 }
295 }
296}
297
298impl TryIntoRequest for CreateFollowup<'_> {
299 fn try_into_request(self) -> Result<Request, Error> {
300 let mut fields = self.fields.map_err(Error::validation)?;
301 let mut request = Request::builder(&Route::ExecuteWebhook {
302 thread_id: None,
303 token: self.token,
304 wait: None,
305 with_components: Some(
306 fields
307 .components
308 .is_some_and(|components| !components.is_empty()),
309 ),
310 webhook_id: self.application_id.get(),
311 });
312
313 request = request.use_authorization_token(false);
316
317 if fields.allowed_mentions.is_none()
319 && let Some(allowed_mentions) = self.http.default_allowed_mentions()
320 {
321 fields.allowed_mentions = Some(Nullable(Some(allowed_mentions)));
322 }
323
324 if !self.attachment_manager.is_empty() {
327 let form = if let Some(payload_json) = fields.payload_json {
328 self.attachment_manager.build_form(payload_json)
329 } else {
330 fields.attachments = Some(self.attachment_manager.get_partial_attachments());
331
332 let fields = crate::json::to_vec(&fields).map_err(Error::json)?;
333
334 self.attachment_manager.build_form(fields.as_ref())
335 };
336
337 request = request.form(form);
338 } else if let Some(payload_json) = fields.payload_json {
339 request = request.body(payload_json.to_vec());
340 } else {
341 request = request.json(&fields);
342 }
343
344 request.build()
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use crate::{client::Client, request::TryIntoRequest};
351 use std::error::Error;
352 use twilight_model::id::Id;
353
354 #[test]
355 fn create_followup_message() -> Result<(), Box<dyn Error>> {
356 let application_id = Id::new(1);
357 let token = "foo".to_owned();
358
359 let client = Client::new(String::new());
360 let req = client
361 .interaction(application_id)
362 .create_followup(&token)
363 .content("test")
364 .try_into_request()?;
365
366 assert!(!req.use_authorization_token());
367
368 Ok(())
369 }
370}