slashook/commands/
responder.rs

1// Copyright 2025 slashook Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::structs::{
9  components::{Component, Components},
10  embeds::Embed,
11  interactions::{ApplicationCommandOptionChoice, Attachments, InteractionCallbackData},
12  messages::{AllowedMentions, Attachment, Message, MessageFlags, MessageReference},
13  polls::PollCreateRequest,
14  utils::File, Snowflake,
15};
16use serde::Serialize;
17use crate::tokio::sync::mpsc;
18use crate::rest::{Rest, RestError};
19
20/// Error for when a response failed due to the interaction having been responded to already.
21#[derive(Debug)]
22pub struct InteractionResponseError;
23impl std::fmt::Display for InteractionResponseError {
24  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25    write!(f, "Interaction has already been responded to.")
26  }
27}
28impl std::error::Error for InteractionResponseError { }
29
30/// Message that can be sent as a response to a command or other interaction
31///
32/// This struct can be easily constructed from a `str`, `String`, [`Embed`](crate::structs::embeds::Embed), [`Components`](crate::structs::components::Components),
33/// [`File`](crate::structs::utils::File) or [`PollCreateRequest`](crate::structs::polls::PollCreateRequest)
34/// with the `From` trait
35#[derive(Serialize, Clone, Debug)]
36pub struct MessageResponse {
37  /// Whether the response is TTS
38  pub tts: Option<bool>,
39  /// Message content (up to 2000 characters)
40  #[serde(skip_serializing_if = "Option::is_none")]
41  pub content: Option<String>,
42  /// Up to 10 embeds (up to 6000 characters)
43  #[serde(skip_serializing_if = "Option::is_none")]
44  pub embeds: Option<Vec<Embed>>,
45  /// [Allowed mentions](AllowedMentions) object
46  #[serde(skip_serializing_if = "Option::is_none")]
47  pub allowed_mentions: Option<AllowedMentions>,
48  /// [Message flags](MessageFlags) combined as a bitfield
49  /// (only [SUPPRESS_EMBEDS](MessageFlags::SUPPRESS_EMBEDS), [EPHEMERAL](MessageFlags::EPHEMERAL), and [SUPPRESS_NOTIFICATIONS](MessageFlags::SUPPRESS_NOTIFICATIONS) can be set for interaction responses)
50  pub flags: Option<MessageFlags>,
51  /// Message components
52  #[serde(skip_serializing_if = "Option::is_none")]
53  pub components: Option<Vec<Component>>,
54  /// Attachment objects with filename and description.\
55  /// Use `files` if you want to upload an attachment.\
56  /// This is for partial attachment objects indicating which to keep when editing.
57  #[serde(skip_serializing_if = "Option::is_none")]
58  pub attachments: Option<Vec<Attachment>>,
59  /// Details about the poll
60  #[serde(skip_serializing_if = "Option::is_none")]
61  pub poll: Option<PollCreateRequest>,
62  /// Include to make your message a reply or a forward (not available for interaction responses)
63  #[serde(skip_serializing_if = "Option::is_none")]
64  pub message_reference: Option<MessageReference>,
65  /// IDs of up to 3 stickers in the server to send in the message
66  #[serde(skip_serializing_if = "Option::is_none")]
67  pub sticker_ids: Option<Vec<Snowflake>>,
68  /// Up to 10 files to send with the response
69  #[serde(skip_serializing)]
70  pub files: Option<Vec<File>>,
71}
72
73impl MessageResponse {
74  /// Set the value of tts for the message
75  /// ```
76  /// # use slashook::commands::MessageResponse;
77  /// let response = MessageResponse::from("This message is text to speech")
78  ///   .set_tts(true);
79  /// assert_eq!(response.tts, Some(true));
80  /// ```
81  pub fn set_tts(mut self, tts: bool) -> Self {
82    self.tts = Some(tts);
83    self
84  }
85
86  /// Set the content of the message
87  /// ```
88  /// # use slashook::commands::MessageResponse;
89  /// let response = MessageResponse::from("This content will be replaced")
90  ///   .set_content("I rule the world!");
91  /// assert_eq!(response.content, Some(String::from("I rule the world!")));
92  /// ```
93  pub fn set_content<T: ToString>(mut self, content: T) -> Self {
94    self.content = Some(content.to_string());
95    self
96  }
97
98  /// Add an embed to the message
99  /// ```
100  /// # use slashook::commands::MessageResponse;
101  /// # use slashook::structs::embeds::Embed;
102  /// let embed = Embed::new().set_description("This is an embed");
103  /// let response = MessageResponse::from("Look at my embed:")
104  ///   .add_embed(embed);
105  /// assert_eq!(response.embeds.unwrap()[0].description, Some(String::from("This is an embed")));
106  /// ```
107  pub fn add_embed(mut self, embed: Embed) -> Self {
108    let mut embeds = self.embeds.unwrap_or_default();
109    embeds.push(embed);
110    self.embeds = Some(embeds);
111    self
112  }
113
114  /// Clear embeds from the message. Sets embeds to an empty Vec which also clears embeds when editing.
115  /// ```
116  /// # use slashook::commands::MessageResponse;
117  /// let response = MessageResponse::from("Embeds cleared")
118  ///   .clear_embeds();
119  /// assert_eq!(response.embeds.unwrap().len(), 0);
120  /// ```
121  pub fn clear_embeds(mut self) -> Self {
122    self.embeds = Some(Vec::new());
123    self
124  }
125
126  /// Set the allowed mentions for the message
127  /// ```
128  /// # use slashook::commands::MessageResponse;
129  /// # use slashook::structs::messages::{AllowedMentions, AllowedMentionType};
130  /// let allowed_mentions = AllowedMentions::new().add_parse(AllowedMentionType::USERS);
131  /// let response = MessageResponse::from("<@1234> Get pinged. Not @everyone or <@&1235> tho.")
132  ///   .set_allowed_mentions(allowed_mentions);
133  /// ```
134  pub fn set_allowed_mentions(mut self, allowed_mentions: AllowedMentions) -> Self {
135    self.allowed_mentions = Some(allowed_mentions);
136    self
137  }
138
139  /// Set the ephemeralness of the message
140  /// ```
141  /// # use slashook::commands::MessageResponse;
142  /// # use slashook::structs::messages::MessageFlags;
143  /// let response = MessageResponse::from("This is for your eyes only!")
144  ///   .set_ephemeral(true);
145  /// assert_eq!(response.flags.unwrap().contains(MessageFlags::EPHEMERAL), true);
146  /// ```
147  pub fn set_ephemeral(mut self, ephemeral: bool) -> Self {
148    let mut flags = self.flags.unwrap_or_else(MessageFlags::empty);
149    flags.set(MessageFlags::EPHEMERAL, ephemeral);
150    self.flags = Some(flags);
151    self
152  }
153
154  /// Set suppress embeds flag
155  /// ```
156  /// # use slashook::commands::MessageResponse;
157  /// # use slashook::structs::messages::MessageFlags;
158  /// let response = MessageResponse::from("No embeds here")
159  ///   .set_suppress_embeds(true);
160  /// assert_eq!(response.flags.unwrap().contains(MessageFlags::SUPPRESS_EMBEDS), true);
161  /// ```
162  pub fn set_suppress_embeds(mut self, suppress: bool) -> Self {
163    let mut flags = self.flags.unwrap_or_else(MessageFlags::empty);
164    flags.set(MessageFlags::SUPPRESS_EMBEDS, suppress);
165    self.flags = Some(flags);
166    self
167  }
168
169  /// Set suppress notifications flag
170  /// ```
171  /// # use slashook::commands::MessageResponse;
172  /// # use slashook::structs::messages::MessageFlags;
173  /// let response = MessageResponse::from("No notifications @here")
174  ///   .set_suppress_notifications(true);
175  /// assert_eq!(response.flags.unwrap().contains(MessageFlags::SUPPRESS_NOTIFICATIONS), true);
176  /// ```
177  pub fn set_suppress_notifications(mut self, suppress: bool) -> Self {
178    let mut flags = self.flags.unwrap_or_else(MessageFlags::empty);
179    flags.set(MessageFlags::SUPPRESS_NOTIFICATIONS, suppress);
180    self.flags = Some(flags);
181    self
182  }
183
184  /// Set voice message flag
185  /// ```no_run
186  /// # use slashook::commands::{MessageResponse, CmdResult};
187  /// # use slashook::structs::{messages::MessageFlags, utils::File};
188  /// # use slashook::tokio::fs::File as TokioFile;
189  /// # #[slashook::main]
190  /// # async fn main() -> CmdResult {
191  /// let file = TokioFile::open("audio.ogg").await?;
192  /// let audio_file = File::from_file("audio.ogg", file).await?
193  ///   .set_duration_secs(1.1799999475479126)
194  ///   .set_waveform("AAM1YAAAAAAAAAA=");
195  /// let response = MessageResponse::from(audio_file).set_as_voice_message(true);
196  /// assert_eq!(response.flags.unwrap().contains(MessageFlags::IS_VOICE_MESSAGE), true);
197  /// # Ok(())
198  /// # }
199  /// ```
200  pub fn set_as_voice_message(mut self, is_voice_message: bool) -> Self {
201    let mut flags = self.flags.unwrap_or_else(MessageFlags::empty);
202    flags.set(MessageFlags::IS_VOICE_MESSAGE, is_voice_message);
203    self.flags = Some(flags);
204    self
205  }
206
207  /// Set the components on the message
208  /// ```
209  /// # use slashook::commands::MessageResponse;
210  /// # use slashook::structs::components::{Components, Button, ButtonStyle};
211  /// let button = Button::new()
212  ///   .set_style(ButtonStyle::DANGER)
213  ///   .set_label("Do not press!")
214  ///   .set_id("example_button", "danger");
215  /// let components = Components::new().add_button(button);
216  /// let response = MessageResponse::from("Ooh! A big red button!")
217  ///   .set_components(components);
218  /// ```
219  pub fn set_components(mut self, components: Components) -> Self {
220    self.components = Some(components.0);
221    self
222  }
223
224  /// Add a file to be sent with the message
225  /// ```no_run
226  /// # use slashook::commands::{MessageResponse, CmdResult};
227  /// # use slashook::structs::utils::File;
228  /// use slashook::tokio::fs::File as TokioFile;
229  /// # #[slashook::main]
230  /// # async fn main() -> CmdResult {
231  /// let file = TokioFile::open("cat.png").await?;
232  /// let msg_file = File::from_file("cat.png", file).await?
233  ///   .set_description("Picture of my cute cat!");
234  /// let response = MessageResponse::from("Here's a picture of my cat")
235  ///   .add_file(msg_file);
236  /// # Ok(())
237  /// # }
238  /// ```
239  pub fn add_file(mut self, file: File) -> Self {
240    let mut files = self.files.unwrap_or_default();
241    files.push(file);
242    self.files = Some(files);
243    self
244  }
245
246  /// Keep an existing attachment when editing
247  /// ```
248  /// # #[macro_use] extern crate slashook;
249  /// # use slashook::commands::{CommandInput, CommandResponder};
250  /// # use slashook::commands::MessageResponse;
251  /// # use slashook::structs::utils::File;
252  /// # use slashook::tokio::fs::File as TokioFile;
253  /// # #[command(name = "example", description = "An example command")]
254  /// # fn example(input: CommandInput, res: CommandResponder) {
255  /// let msg_file = File::from_file("cat.png", TokioFile::open("cat.png").await?).await?;
256  /// let msg_file2 = File::from_file("cat2.png", TokioFile::open("cat2.png").await?).await?;
257  ///
258  /// res.defer(false).await?;
259  ///
260  /// let response = MessageResponse::from("Here's a picture of my cat")
261  ///   .add_file(msg_file);
262  /// let msg = res.send_followup_message(response).await?;
263  ///
264  /// let edit_response = MessageResponse::from("And I added the other cat too!")
265  ///   .keep_attachment(&msg.attachments.get(0).unwrap().id)
266  ///   .add_file(msg_file2);
267  /// res.edit_original_message(edit_response).await?;
268  /// # }
269  /// ```
270  pub fn keep_attachment<T: ToString>(mut self, attachment_id: T) -> Self {
271    let mut attachments = self.attachments.unwrap_or_default();
272    attachments.push(Attachment::keep_with_id(attachment_id));
273    self.attachments = Some(attachments);
274    self
275  }
276
277  /// Clear attachments from the message. Sets attachments to an empty Vec which also deletes attachments when editing.
278  /// ```
279  /// # use slashook::commands::MessageResponse;
280  /// let response = MessageResponse::from("Attachments deleted")
281  ///   .clear_attachments();
282  /// assert_eq!(response.attachments.unwrap().len(), 0);
283  /// ```
284  pub fn clear_attachments(mut self) -> Self {
285    self.attachments = Some(Vec::new());
286    self
287  }
288
289  /// Add a poll to the message
290  /// ```
291  /// # use slashook::commands::MessageResponse;
292  /// # use slashook::structs::{polls::{PollCreateRequest, PollAnswer}, Emoji};
293  /// let response = MessageResponse::from("This message will contain a poll!")
294  ///   .set_poll(PollCreateRequest::new("Is this a good poll?")
295  ///     .add_answer(PollAnswer::new().set_text("Yes").set_emoji(Emoji::new_standard_emoji("✅")))
296  ///     .add_answer(PollAnswer::from("No").set_emoji(Emoji::new_custom_emoji("567088349484023818", "redtick", false)))
297  ///     .add_answer("Maybe")
298  ///     .set_duration(1)
299  ///   );
300  /// ```
301  pub fn set_poll(mut self, poll: PollCreateRequest) -> Self {
302    self.poll = Some(poll);
303    self
304  }
305
306  /// Set a reply or forward
307  /// ```
308  /// # use slashook::commands::MessageResponse;
309  /// # use slashook::structs::messages::MessageReference;
310  /// let response = MessageResponse::from("This is a reply")
311  ///   .set_message_reference(MessageReference::new_reply("916413462467465246"));
312  /// assert_eq!(response.message_reference.unwrap().message_id, Some(String::from("916413462467465246")));
313  /// ```
314  pub fn set_message_reference(mut self, message_reference: MessageReference) -> Self {
315    self.message_reference = Some(message_reference);
316    self
317  }
318
319  /// Add a sticker to the message
320  /// ```
321  /// # use slashook::commands::MessageResponse;
322  /// let response = MessageResponse::from(":)")
323  ///   .add_sticker("749044136589393960");
324  /// assert_eq!(response.sticker_ids.unwrap().remove(0), String::from("749044136589393960"));
325  /// ```
326  pub fn add_sticker<T: ToString>(mut self, sticker_id: T) -> Self {
327    let mut sticker_ids = self.sticker_ids.unwrap_or_default();
328    sticker_ids.push(sticker_id.to_string());
329    self.sticker_ids = Some(sticker_ids);
330    self
331  }
332}
333
334/// A modal that can be opened for user input
335#[derive(Clone, Debug)]
336pub struct Modal {
337  /// a developer-defined identifier for the component, max 100 characters
338  pub custom_id: String,
339  /// The title of the popup modal
340  pub title: String,
341  /// The components that make up the modal
342  pub components: Vec<Component>
343}
344
345impl Modal {
346  /// Creates a new modal.\
347  /// The command argument is used by the library to choose which command to run when the modal is submitted.
348  /// The custom_id is formatted as `command/id`
349  /// ```
350  /// # use slashook::commands::Modal;
351  /// let modal = Modal::new("example_command", "modal1", "Please fill this form");
352  /// ```
353  pub fn new<T: ToString, U: ToString, V: ToString>(command: T, id: U, title: V) -> Self {
354    Self {
355      custom_id: format!("{}/{}", command.to_string(), id.to_string()),
356      title: title.to_string(),
357      components: Vec::new()
358    }
359  }
360
361  /// Set the components on the modal
362  /// ```
363  /// # use slashook::commands::Modal;
364  /// # use slashook::structs::components::{Components, TextInput};
365  /// let text_input = TextInput::new()
366  ///   .set_label("Tell us something")
367  ///   .set_id("input");
368  /// let components = Components::new().add_text_input(text_input);
369  /// let modal = Modal::new("example_command", "modal1", "Please fill this form")
370  ///   .set_components(components);
371  /// ```
372  pub fn set_components(mut self, components: Components) -> Self {
373    self.components = components.0;
374    self
375  }
376}
377
378#[derive(Debug)]
379pub enum CommandResponse {
380  DeferMessage(MessageFlags),
381  SendMessage(MessageResponse),
382  DeferUpdate,
383  UpdateMessage(MessageResponse),
384  AutocompleteResult(Vec<ApplicationCommandOptionChoice>),
385  Modal(Modal),
386  LaunchActivity,
387}
388
389/// Struct with methods for responding to interactions
390#[derive(Debug)]
391pub struct CommandResponder {
392  pub(crate) tx: mpsc::UnboundedSender<CommandResponse>,
393  pub(crate) id: String,
394  pub(crate) token: String,
395  pub(crate) rest: Rest
396}
397
398impl CommandResponder {
399  /// Respond to an interaction with a message.\
400  /// If interaction has already been responded to, this function will call [`send_followup_message`](CommandResponder::send_followup_message) instead and a message can only be returned in this case.
401  /// ```
402  /// # #[macro_use] extern crate slashook;
403  /// # use slashook::commands::{CommandInput, CommandResponder};
404  /// ##[command(name = "example", description = "An example command")]
405  /// fn example(input: CommandInput, res: CommandResponder) {
406  ///   res.send_message("Hello!").await?;
407  /// }
408  /// ```
409  pub async fn send_message<T: Into<MessageResponse>>(&self, response: T) -> Result<Option<Message>, RestError> {
410    let response = response.into();
411    match self.tx.send(CommandResponse::SendMessage(response)) {
412      Ok(_) => {
413        self.tx.closed().await;
414        Ok(None)
415      },
416      Err(err) => {
417        if let CommandResponse::SendMessage(response) = err.0 {
418          return self.send_followup_message(response).await.map(Some);
419        }
420        Ok(None)
421      }
422    }
423  }
424
425  /// Respond to an interaction by editing the original message.\
426  /// If interaction has already been responded to, this function will call [`edit_original_message`](CommandResponder::edit_original_message) instead and a message can only be returned in this case.
427  /// ```
428  /// # #[macro_use] extern crate slashook;
429  /// # use slashook::commands::{CommandInput, CommandResponder, MessageResponse};
430  /// # use slashook::structs::components::{Components, Button};
431  /// ##[command(name = "example_button", ignore = true)]
432  /// fn example(input: CommandInput, res: CommandResponder) {
433  ///   res.update_message("Button was clicked!").await?;
434  /// }
435  /// ```
436  pub async fn update_message<T: Into<MessageResponse>>(&self, response: T) -> Result<Option<Message>, RestError> {
437    let response = response.into();
438    match self.tx.send(CommandResponse::UpdateMessage(response)) {
439      Ok(_) => {
440        self.tx.closed().await;
441        Ok(None)
442      },
443      Err(err) => {
444        if let CommandResponse::UpdateMessage(response) = err.0 {
445          return self.edit_original_message(response).await.map(Some);
446        }
447        Ok(None)
448      }
449    }
450  }
451
452  /// Give yourself more execution time.\
453  /// If you don't respond within 3 seconds, Discord will disconnect and tell the user the interaction failed to run.
454  /// By deferring, Discord will tell the user your bot is "thinking" and allow you to take your time. You can use the `send_followup_message` or `edit_original_message` methods to send the response.\
455  /// The ephemeralness set here will be passed on to your first follow-up, no matter what ephemeralness you set there.
456  /// ```
457  /// # #[macro_use] extern crate slashook;
458  /// # use slashook::commands::{CommandInput, CommandResponder, MessageResponse};
459  /// ##[command(name = "example", description = "An example command")]
460  /// fn example(input: CommandInput, res: CommandResponder) {
461  ///   res.defer(false).await?;
462  ///   // Do something that takes longer than 3s
463  ///   res.send_followup_message("Thank you for your patience!").await?;
464  /// }
465  /// ```
466  pub async fn defer(&self, ephemeral: bool) -> Result<(), InteractionResponseError> {
467    let mut flags = MessageFlags::empty();
468    flags.set(MessageFlags::EPHEMERAL, ephemeral);
469    self.tx.send(CommandResponse::DeferMessage(flags)).map_err(|_| InteractionResponseError)?;
470    self.tx.closed().await;
471    Ok(())
472  }
473
474  /// Much like `defer` but for component interactions and it shows nothing visibly to the user.
475  /// ```
476  /// # #[macro_use] extern crate slashook;
477  /// # use slashook::commands::{CommandInput, CommandResponder, MessageResponse};
478  /// ##[command(name = "example_button", ignore = true)]
479  /// fn example(input: CommandInput, res: CommandResponder) {
480  ///   res.defer_update().await?;
481  ///   // Do something that takes longer than 3s
482  ///   res.edit_original_message("Finally it changed!").await?;
483  /// }
484  /// ```
485  pub async fn defer_update(&self) -> Result<(), InteractionResponseError> {
486    self.tx.send(CommandResponse::DeferUpdate).map_err(|_| InteractionResponseError)?;
487    self.tx.closed().await;
488    Ok(())
489  }
490
491  /// Respond to an autocomplete interaction with autocomplete choices
492  /// ```
493  /// # #[macro_use] extern crate slashook;
494  /// # use slashook::commands::{CommandInput, CommandResponder, MessageResponse};
495  /// # use slashook::structs::interactions::{ApplicationCommandOptionChoice, InteractionOptionType};
496  /// ##[command(name = "example", description = "An example command", options = [{
497  ///   name = "choice", description = "Choose an option",
498  ///   autocomplete = true, option_type = InteractionOptionType::STRING
499  /// }])]
500  /// fn example(input: CommandInput, res: CommandResponder) {
501  ///   if input.is_autocomplete() {
502  ///     let search = input.args.get(&input.focused.unwrap()).unwrap().as_string().unwrap();
503  ///     // Use the current input to fetch or filter choices
504  ///     let choices = vec![
505  ///       ApplicationCommandOptionChoice::new("An autocompleted choice", "autocomplete1"),
506  ///       ApplicationCommandOptionChoice::new("Another autocompleted choice", "autocomplete2")
507  ///     ];
508  ///     return res.autocomplete(choices).await?;
509  ///   }
510  /// }
511  /// ```
512  pub async fn autocomplete(&self, results: Vec<ApplicationCommandOptionChoice>) -> Result<(), InteractionResponseError> {
513    self.tx.send(CommandResponse::AutocompleteResult(results)).map_err(|_| InteractionResponseError)?;
514    self.tx.closed().await;
515    Ok(())
516  }
517
518  /// Respond to an interaction with a modal
519  /// ```
520  /// # #[macro_use] extern crate slashook;
521  /// # use slashook::commands::{CommandInput, CommandResponder, MessageResponse, Modal};
522  /// # use slashook::structs::components::{Components, TextInput};
523  /// ##[command(name = "example", description = "An example command")]
524  /// fn example(input: CommandInput, res: CommandResponder) {
525  ///   let text_input = TextInput::new()
526  ///     .set_label("Tell us something")
527  ///     .set_id("input");
528  ///   let components = Components::new().add_text_input(text_input);
529  ///   let modal = Modal::new("example_command", "modal1", "Please fill this form")
530  ///     .set_components(components);
531  ///   return res.open_modal(modal).await?;
532  /// }
533  /// ```
534  pub async fn open_modal(&self, modal: Modal) -> Result<(), InteractionResponseError> {
535    self.tx.send(CommandResponse::Modal(modal)).map_err(|_| InteractionResponseError)?;
536    self.tx.closed().await;
537    Ok(())
538  }
539
540  /// Respond to an interaction by launching the activity associated with the app.
541  /// ```
542  /// # #[macro_use] extern crate slashook;
543  /// # use slashook::commands::{CommandInput, CommandResponder};
544  /// # use slashook::structs::interactions::{ApplicationCommandType, ApplicationCommandHandlerType};
545  /// ##[command(
546  ///   name = "launch",
547  ///   command_type = ApplicationCommandType::PRIMARY_ENTRY_POINT,
548  ///   handler = ApplicationCommandHandlerType::APP_HANDLER
549  /// )]
550  /// fn launch(input: CommandInput, res: CommandResponder) {
551  ///   return res.launch_activity().await?;
552  /// }
553  /// ```
554  pub async fn launch_activity(&self) -> Result<(), InteractionResponseError> {
555    self.tx.send(CommandResponse::LaunchActivity).map_err(|_| InteractionResponseError)?;
556    self.tx.closed().await;
557    Ok(())
558  }
559
560  /// Send more messages after the initial response
561  /// ```
562  /// # #[macro_use] extern crate slashook;
563  /// # use slashook::commands::{CommandInput, CommandResponder, MessageResponse};
564  /// ##[command(name = "example", description = "An example command")]
565  /// fn example(input: CommandInput, res: CommandResponder) {
566  ///   res.send_message("First message!").await?;
567  ///   res.send_followup_message("Second message!").await?;
568  /// }
569  /// ```
570  pub async fn send_followup_message<T: Into<MessageResponse>>(&self, response: T) -> Result<Message, RestError> {
571    let mut response = response.into();
572    let files = response.files.take();
573    let msg: InteractionCallbackData = response.into();
574    let path = format!("webhooks/{}/{}", self.id, self.token);
575    if let Some(files) = files {
576      self.rest.post_files(path, msg, files).await
577    } else {
578      self.rest.post(path, msg).await
579    }
580  }
581
582  /// Edits a follow-up message
583  /// ```
584  /// # #[macro_use] extern crate slashook;
585  /// # use slashook::commands::{CommandInput, CommandResponder, MessageResponse};
586  /// ##[command(name = "example", description = "An example command")]
587  /// fn example(input: CommandInput, res: CommandResponder) {
588  ///   res.send_message("First message!").await?;
589  ///   let msg = res.send_followup_message("Second message!").await?;
590  ///   res.edit_followup_message(msg.id.unwrap(), "Second message but edited!").await?;
591  /// }
592  /// ```
593  pub async fn edit_followup_message<T: Into<MessageResponse>>(&self, id: String, response: T) -> Result<Message, RestError> {
594    let mut response = response.into();
595    let files = response.files.take();
596    let msg: InteractionCallbackData = response.into();
597    let path = format!("webhooks/{}/{}/messages/{}", self.id, self.token, id);
598    if let Some(files) = files {
599      self.rest.patch_files(path, msg, files).await
600    } else {
601      self.rest.patch(path, msg).await
602    }
603  }
604
605  /// Edits the original message\
606  /// Same as running `edit_followup_message` with id of `@original`
607  pub async fn edit_original_message<T: Into<MessageResponse>>(&self, response: T) -> Result<Message, RestError> {
608    self.edit_followup_message(String::from("@original"), response).await
609  }
610
611  /// Gets a follow-up message
612  pub async fn get_followup_message(&self, id: String) -> Result<Message, RestError> {
613    self.rest.get(format!("webhooks/{}/{}/messages/{}", self.id, self.token, id)).await
614  }
615
616  /// Gets the original message\
617  /// Same as running `get_followup_message` with id of `@original`
618  /// ```
619  /// # #[macro_use] extern crate slashook;
620  /// # use slashook::commands::{CommandInput, CommandResponder, MessageResponse};
621  /// ##[command(name = "example", description = "An example command")]
622  /// fn example(input: CommandInput, res: CommandResponder) {
623  ///   res.send_message("First message!").await?;
624  ///   let msg = res.get_original_message().await?;
625  ///   println!("I responded with {}", msg.content);
626  /// }
627  /// ```
628  pub async fn get_original_message(&self) -> Result<Message, RestError> {
629    self.get_followup_message(String::from("@original")).await
630  }
631
632  /// Deletes a follow-up message
633  /// ```
634  /// # #[macro_use] extern crate slashook;
635  /// # use slashook::commands::{CommandInput, CommandResponder, MessageResponse};
636  /// ##[command(name = "example", description = "An example command")]
637  /// fn example(input: CommandInput, res: CommandResponder) {
638  ///   res.send_message("First message!").await?;
639  ///   let msg = res.send_followup_message("If you see me say hi").await?;
640  ///   res.delete_followup_message(msg.id.unwrap()).await?;
641  /// }
642  /// ```
643  pub async fn delete_followup_message(&self, id: String) -> Result<(), RestError> {
644    self.rest.delete(format!("webhooks/{}/{}/messages/{}", self.id, self.token, id)).await
645  }
646
647  /// Deletes the original message\
648  /// Same as running `delete_followup_message` with id of `@original`
649  pub async fn delete_original_message(&self) -> Result<(), RestError> {
650    self.delete_followup_message(String::from("@original")).await
651  }
652}
653
654impl From<&str> for MessageResponse {
655  fn from(s: &str) -> MessageResponse {
656    MessageResponse {
657      tts: Some(false),
658      content: Some(String::from(s)),
659      embeds: None,
660      allowed_mentions: None,
661      flags: None,
662      components: None,
663      attachments: None,
664      poll: None,
665      message_reference: None,
666      sticker_ids: None,
667      files: None,
668    }
669  }
670}
671
672impl From<String> for MessageResponse {
673  fn from(s: String) -> MessageResponse {
674    MessageResponse {
675      tts: Some(false),
676      content: Some(s),
677      embeds: None,
678      allowed_mentions: None,
679      flags: None,
680      components: None,
681      attachments: None,
682      poll: None,
683      message_reference: None,
684      sticker_ids: None,
685      files: None,
686    }
687  }
688}
689
690impl From<Embed> for MessageResponse {
691  fn from(e: Embed) -> MessageResponse {
692    MessageResponse {
693      tts: Some(false),
694      content: None,
695      embeds: Some(vec![e]),
696      allowed_mentions: None,
697      flags: None,
698      components: None,
699      attachments: None,
700      poll: None,
701      message_reference: None,
702      sticker_ids: None,
703      files: None,
704    }
705  }
706}
707
708impl From<Vec<Embed>> for MessageResponse {
709  fn from(e: Vec<Embed>) -> MessageResponse {
710    MessageResponse {
711      tts: Some(false),
712      content: None,
713      embeds: Some(e),
714      allowed_mentions: None,
715      flags: None,
716      components: None,
717      attachments: None,
718      poll: None,
719      message_reference: None,
720      sticker_ids: None,
721      files: None,
722    }
723  }
724}
725
726impl From<Components> for MessageResponse {
727  fn from(c: Components) -> MessageResponse {
728    MessageResponse {
729      tts: Some(false),
730      content: None,
731      embeds: None,
732      allowed_mentions: None,
733      flags: None,
734      components: Some(c.0),
735      attachments: None,
736      poll: None,
737      message_reference: None,
738      sticker_ids: None,
739      files: None,
740    }
741  }
742}
743
744impl From<File> for MessageResponse {
745  fn from(f: File) -> MessageResponse {
746    MessageResponse {
747      tts: Some(false),
748      content: None,
749      embeds: None,
750      allowed_mentions: None,
751      flags: None,
752      components: None,
753      attachments: None,
754      poll: None,
755      message_reference: None,
756      sticker_ids: None,
757      files: Some(vec![f]),
758    }
759  }
760}
761
762impl From<Vec<File>> for MessageResponse {
763  fn from(f: Vec<File>) -> MessageResponse {
764    MessageResponse {
765      tts: Some(false),
766      content: None,
767      embeds: None,
768      allowed_mentions: None,
769      flags: None,
770      components: None,
771      attachments: None,
772      poll: None,
773      message_reference: None,
774      sticker_ids: None,
775      files: Some(f),
776    }
777  }
778}
779
780impl From<PollCreateRequest> for MessageResponse {
781  fn from(poll: PollCreateRequest) -> MessageResponse {
782    MessageResponse {
783      tts: Some(false),
784      content: None,
785      embeds: None,
786      allowed_mentions: None,
787      flags: None,
788      components: None,
789      attachments: None,
790      poll: Some(poll),
791      message_reference: None,
792      sticker_ids: None,
793      files: None,
794    }
795  }
796}
797
798impl From<MessageReference> for MessageResponse {
799  fn from(reference: MessageReference) -> MessageResponse {
800    MessageResponse {
801      tts: Some(false),
802      content: None,
803      embeds: None,
804      allowed_mentions: None,
805      flags: None,
806      components: None,
807      attachments: None,
808      poll: None,
809      message_reference: Some(reference),
810      sticker_ids: None,
811      files: None,
812    }
813  }
814}
815
816impl Attachments for MessageResponse {
817  fn take_attachments(&mut self) -> Vec<Attachment> {
818    self.attachments.take().unwrap_or_default()
819  }
820
821  fn set_attachments(&mut self, attachments: Vec<Attachment>) -> &mut Self {
822    self.attachments = Some(attachments);
823    self
824  }
825}