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 poll::Poll,
18};
19use twilight_validate::message::{
20 attachment as validate_attachment, components as validate_components,
21 content as validate_content, embeds as validate_embeds, MessageValidationError,
22};
23
24#[derive(Serialize)]
25struct CreateFollowupFields<'a> {
26 #[serde(skip_serializing_if = "Option::is_none")]
27 allowed_mentions: Option<Nullable<&'a AllowedMentions>>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 attachments: Option<Vec<PartialAttachment<'a>>>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 components: Option<&'a [Component]>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 content: Option<&'a str>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 embeds: Option<&'a [Embed]>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 payload_json: Option<&'a [u8]>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 tts: Option<bool>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 flags: Option<MessageFlags>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 poll: Option<Poll>,
44}
45
46#[must_use = "requests must be configured and executed"]
77pub struct CreateFollowup<'a> {
78 application_id: Id<ApplicationMarker>,
79 attachment_manager: AttachmentManager<'a>,
80 fields: Result<CreateFollowupFields<'a>, MessageValidationError>,
81 http: &'a Client,
82 token: &'a str,
83}
84
85impl<'a> CreateFollowup<'a> {
86 pub(crate) const fn new(
87 http: &'a Client,
88 application_id: Id<ApplicationMarker>,
89 token: &'a str,
90 ) -> Self {
91 Self {
92 application_id,
93 attachment_manager: AttachmentManager::new(),
94 fields: Ok(CreateFollowupFields {
95 allowed_mentions: None,
96 attachments: None,
97 components: None,
98 content: None,
99 embeds: None,
100 payload_json: None,
101 tts: None,
102 flags: None,
103 poll: None,
104 }),
105 http,
106 token,
107 }
108 }
109
110 pub fn allowed_mentions(mut self, allowed_mentions: Option<&'a AllowedMentions>) -> Self {
115 if let Ok(fields) = self.fields.as_mut() {
116 fields.allowed_mentions = Some(Nullable(allowed_mentions));
117 }
118
119 self
120 }
121
122 pub fn attachments(mut self, attachments: &'a [Attachment]) -> Self {
137 if self.fields.is_ok() {
138 if let Err(source) = attachments.iter().try_for_each(validate_attachment) {
139 self.fields = Err(source);
140 } else {
141 self.attachment_manager = self
142 .attachment_manager
143 .set_files(attachments.iter().collect());
144 }
145 }
146
147 self
148 }
149
150 pub fn components(mut self, components: &'a [Component]) -> Self {
160 self.fields = self.fields.and_then(|mut fields| {
161 validate_components(components)?;
162 fields.components = Some(components);
163
164 Ok(fields)
165 });
166
167 self
168 }
169
170 pub fn content(mut self, content: &'a str) -> Self {
181 self.fields = self.fields.and_then(|mut fields| {
182 validate_content(content)?;
183 fields.content = Some(content);
184
185 Ok(fields)
186 });
187
188 self
189 }
190
191 pub fn embeds(mut self, embeds: &'a [Embed]) -> Self {
212 self.fields = self.fields.and_then(|mut fields| {
213 validate_embeds(embeds)?;
214 fields.embeds = Some(embeds);
215
216 Ok(fields)
217 });
218
219 self
220 }
221
222 pub fn flags(mut self, flags: MessageFlags) -> Self {
229 if let Ok(fields) = self.fields.as_mut() {
230 fields.flags = Some(flags);
231 }
232
233 self
234 }
235
236 pub fn payload_json(mut self, payload_json: &'a [u8]) -> Self {
249 if let Ok(fields) = self.fields.as_mut() {
250 fields.payload_json = Some(payload_json);
251 }
252
253 self
254 }
255
256 pub fn tts(mut self, tts: bool) -> Self {
258 if let Ok(fields) = self.fields.as_mut() {
259 fields.tts = Some(tts);
260 }
261
262 self
263 }
264
265 pub fn poll(mut self, poll: Poll) -> Self {
267 if let Ok(fields) = self.fields.as_mut() {
268 fields.poll = Some(poll);
269 }
270
271 self
272 }
273}
274
275impl IntoFuture for CreateFollowup<'_> {
276 type Output = Result<Response<Message>, Error>;
277
278 type IntoFuture = ResponseFuture<Message>;
279
280 fn into_future(self) -> Self::IntoFuture {
281 let http = self.http;
282
283 match self.try_into_request() {
284 Ok(request) => http.request(request),
285 Err(source) => ResponseFuture::error(source),
286 }
287 }
288}
289
290impl TryIntoRequest for CreateFollowup<'_> {
291 fn try_into_request(self) -> Result<Request, Error> {
292 let mut fields = self.fields.map_err(Error::validation)?;
293 let mut request = Request::builder(&Route::ExecuteWebhook {
294 thread_id: None,
295 token: self.token,
296 wait: None,
297 webhook_id: self.application_id.get(),
298 });
299
300 request = request.use_authorization_token(false);
303
304 if fields.allowed_mentions.is_none() {
306 if let Some(allowed_mentions) = self.http.default_allowed_mentions() {
307 fields.allowed_mentions = Some(Nullable(Some(allowed_mentions)));
308 }
309 }
310
311 if !self.attachment_manager.is_empty() {
314 let form = if let Some(payload_json) = fields.payload_json {
315 self.attachment_manager.build_form(payload_json)
316 } else {
317 fields.attachments = Some(self.attachment_manager.get_partial_attachments());
318
319 let fields = crate::json::to_vec(&fields).map_err(Error::json)?;
320
321 self.attachment_manager.build_form(fields.as_ref())
322 };
323
324 request = request.form(form);
325 } else if let Some(payload_json) = fields.payload_json {
326 request = request.body(payload_json.to_vec());
327 } else {
328 request = request.json(&fields);
329 }
330
331 request.build()
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use crate::{client::Client, request::TryIntoRequest};
338 use std::error::Error;
339 use twilight_http_ratelimiting::Path;
340 use twilight_model::id::Id;
341
342 #[test]
343 fn create_followup_message() -> Result<(), Box<dyn Error>> {
344 let application_id = Id::new(1);
345 let token = "foo".to_owned();
346
347 let client = Client::new(String::new());
348 let req = client
349 .interaction(application_id)
350 .create_followup(&token)
351 .content("test")
352 .try_into_request()?;
353
354 assert!(!req.use_authorization_token());
355 assert_eq!(
356 &Path::WebhooksIdToken(application_id.get(), token),
357 req.ratelimit_path()
358 );
359
360 Ok(())
361 }
362}