twilight_http/request/application/interaction/
create_followup.rs1use crate::{
2 client::Client,
3 error::Error,
4 request::{
5 attachment::{AttachmentManager, PartialAttachment},
6 Nullable, Request, TryIntoRequest,
7 },
8 response::{Response, ResponseFuture},
9 routing::Route,
10};
11use serde::Serialize;
12use std::future::IntoFuture;
13use twilight_model::{
14 channel::message::{AllowedMentions, Component, Embed, Message, MessageFlags},
15 http::attachment::Attachment,
16 id::{marker::ApplicationMarker, Id},
17};
18use twilight_validate::message::{
19 attachment as validate_attachment, components as validate_components,
20 content as validate_content, embeds as validate_embeds, MessageValidationError,
21};
22
23#[derive(Serialize)]
24struct CreateFollowupFields<'a> {
25 #[serde(skip_serializing_if = "Option::is_none")]
26 allowed_mentions: Option<Nullable<&'a AllowedMentions>>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 attachments: Option<Vec<PartialAttachment<'a>>>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 components: Option<&'a [Component]>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 content: Option<&'a str>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 embeds: Option<&'a [Embed]>,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 payload_json: Option<&'a [u8]>,
37 #[serde(skip_serializing_if = "Option::is_none")]
38 tts: Option<bool>,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 flags: Option<MessageFlags>,
41}
42
43#[must_use = "requests must be configured and executed"]
74pub struct CreateFollowup<'a> {
75 application_id: Id<ApplicationMarker>,
76 attachment_manager: AttachmentManager<'a>,
77 fields: Result<CreateFollowupFields<'a>, MessageValidationError>,
78 http: &'a Client,
79 token: &'a str,
80}
81
82impl<'a> CreateFollowup<'a> {
83 pub(crate) const fn new(
84 http: &'a Client,
85 application_id: Id<ApplicationMarker>,
86 token: &'a str,
87 ) -> Self {
88 Self {
89 application_id,
90 attachment_manager: AttachmentManager::new(),
91 fields: Ok(CreateFollowupFields {
92 allowed_mentions: None,
93 attachments: None,
94 components: None,
95 content: None,
96 embeds: None,
97 payload_json: None,
98 tts: None,
99 flags: None,
100 }),
101 http,
102 token,
103 }
104 }
105
106 pub fn allowed_mentions(mut self, allowed_mentions: Option<&'a AllowedMentions>) -> Self {
111 if let Ok(fields) = self.fields.as_mut() {
112 fields.allowed_mentions = Some(Nullable(allowed_mentions));
113 }
114
115 self
116 }
117
118 pub fn attachments(mut self, attachments: &'a [Attachment]) -> Self {
133 if self.fields.is_ok() {
134 if let Err(source) = attachments.iter().try_for_each(validate_attachment) {
135 self.fields = Err(source);
136 } else {
137 self.attachment_manager = self
138 .attachment_manager
139 .set_files(attachments.iter().collect());
140 }
141 }
142
143 self
144 }
145
146 pub fn components(mut self, components: &'a [Component]) -> Self {
156 self.fields = self.fields.and_then(|mut fields| {
157 validate_components(components)?;
158 fields.components = Some(components);
159
160 Ok(fields)
161 });
162
163 self
164 }
165
166 pub fn content(mut self, content: &'a str) -> Self {
177 self.fields = self.fields.and_then(|mut fields| {
178 validate_content(content)?;
179 fields.content = Some(content);
180
181 Ok(fields)
182 });
183
184 self
185 }
186
187 pub fn embeds(mut self, embeds: &'a [Embed]) -> Self {
208 self.fields = self.fields.and_then(|mut fields| {
209 validate_embeds(embeds)?;
210 fields.embeds = Some(embeds);
211
212 Ok(fields)
213 });
214
215 self
216 }
217
218 pub fn flags(mut self, flags: MessageFlags) -> Self {
225 if let Ok(fields) = self.fields.as_mut() {
226 fields.flags = Some(flags);
227 }
228
229 self
230 }
231
232 pub fn payload_json(mut self, payload_json: &'a [u8]) -> Self {
245 if let Ok(fields) = self.fields.as_mut() {
246 fields.payload_json = Some(payload_json);
247 }
248
249 self
250 }
251
252 pub fn tts(mut self, tts: bool) -> Self {
254 if let Ok(fields) = self.fields.as_mut() {
255 fields.tts = Some(tts);
256 }
257
258 self
259 }
260}
261
262impl IntoFuture for CreateFollowup<'_> {
263 type Output = Result<Response<Message>, Error>;
264
265 type IntoFuture = ResponseFuture<Message>;
266
267 fn into_future(self) -> Self::IntoFuture {
268 let http = self.http;
269
270 match self.try_into_request() {
271 Ok(request) => http.request(request),
272 Err(source) => ResponseFuture::error(source),
273 }
274 }
275}
276
277impl TryIntoRequest for CreateFollowup<'_> {
278 fn try_into_request(self) -> Result<Request, Error> {
279 let mut fields = self.fields.map_err(Error::validation)?;
280 let mut request = Request::builder(&Route::ExecuteWebhook {
281 thread_id: None,
282 token: self.token,
283 wait: None,
284 webhook_id: self.application_id.get(),
285 });
286
287 request = request.use_authorization_token(false);
290
291 if fields.allowed_mentions.is_none() {
293 if let Some(allowed_mentions) = self.http.default_allowed_mentions() {
294 fields.allowed_mentions = Some(Nullable(Some(allowed_mentions)));
295 }
296 }
297
298 if !self.attachment_manager.is_empty() {
301 let form = if let Some(payload_json) = fields.payload_json {
302 self.attachment_manager.build_form(payload_json)
303 } else {
304 fields.attachments = Some(self.attachment_manager.get_partial_attachments());
305
306 let fields = crate::json::to_vec(&fields).map_err(Error::json)?;
307
308 self.attachment_manager.build_form(fields.as_ref())
309 };
310
311 request = request.form(form);
312 } else if let Some(payload_json) = fields.payload_json {
313 request = request.body(payload_json.to_vec());
314 } else {
315 request = request.json(&fields);
316 }
317
318 request.build()
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use crate::{client::Client, request::TryIntoRequest};
325 use std::error::Error;
326 use twilight_http_ratelimiting::Path;
327 use twilight_model::id::Id;
328
329 #[test]
330 fn create_followup_message() -> Result<(), Box<dyn Error>> {
331 let application_id = Id::new(1);
332 let token = "foo".to_owned();
333
334 let client = Client::new(String::new());
335 let req = client
336 .interaction(application_id)
337 .create_followup(&token)
338 .content("test")
339 .try_into_request()?;
340
341 assert!(!req.use_authorization_token());
342 assert_eq!(
343 &Path::WebhooksIdToken(application_id.get(), token),
344 req.ratelimit_path()
345 );
346
347 Ok(())
348 }
349}