serenity/model/application/
modal_interaction.rs

1use serde::Serialize;
2
3#[cfg(feature = "model")]
4use crate::builder::{
5    Builder,
6    CreateInteractionResponse,
7    CreateInteractionResponseFollowup,
8    CreateInteractionResponseMessage,
9    EditInteractionResponse,
10};
11#[cfg(feature = "model")]
12use crate::http::{CacheHttp, Http};
13use crate::internal::prelude::*;
14use crate::model::prelude::*;
15
16/// An interaction triggered by a modal submit.
17///
18/// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object).
19#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
20#[derive(Clone, Debug, Deserialize, Serialize)]
21#[serde(remote = "Self")]
22#[non_exhaustive]
23pub struct ModalInteraction {
24    /// Id of the interaction.
25    pub id: InteractionId,
26    /// Id of the application this interaction is for.
27    pub application_id: ApplicationId,
28    /// The data of the interaction which was triggered.
29    pub data: ModalInteractionData,
30    /// The guild Id this interaction was sent from, if there is one.
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub guild_id: Option<GuildId>,
33    /// Channel that the interaction was sent from.
34    pub channel: Option<PartialChannel>,
35    /// The channel Id this interaction was sent from.
36    pub channel_id: ChannelId,
37    /// The `member` data for the invoking user.
38    ///
39    /// **Note**: It is only present if the interaction is triggered in a guild.
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub member: Option<Member>,
42    /// The `user` object for the invoking user.
43    #[serde(default)]
44    pub user: User,
45    /// A continuation token for responding to the interaction.
46    pub token: String,
47    /// Always `1`.
48    pub version: u8,
49    /// The message this interaction was triggered by
50    ///
51    /// **Note**: Does not exist if the modal interaction originates from an application command
52    /// interaction
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub message: Option<Box<Message>>,
55    /// Permissions the app or bot has within the channel the interaction was sent from.
56    pub app_permissions: Option<Permissions>,
57    /// The selected language of the invoking user.
58    pub locale: String,
59    /// The guild's preferred locale.
60    pub guild_locale: Option<String>,
61    /// For monetized applications, any entitlements of the invoking user.
62    pub entitlements: Vec<Entitlement>,
63}
64
65#[cfg(feature = "model")]
66impl ModalInteraction {
67    /// Gets the interaction response.
68    ///
69    /// # Errors
70    ///
71    /// Returns an [`Error::Http`] if there is no interaction response.
72    pub async fn get_response(&self, http: impl AsRef<Http>) -> Result<Message> {
73        http.as_ref().get_original_interaction_response(&self.token).await
74    }
75
76    /// Creates a response to the interaction received.
77    ///
78    /// **Note**: Message contents must be under 2000 unicode code points.
79    ///
80    /// # Errors
81    ///
82    /// Returns an [`Error::Model`] if the message content is too long. May also return an
83    /// [`Error::Http`] if the API returns an error, or an [`Error::Json`] if there is an error in
84    /// deserializing the API response.
85    pub async fn create_response(
86        &self,
87        cache_http: impl CacheHttp,
88        builder: CreateInteractionResponse,
89    ) -> Result<()> {
90        builder.execute(cache_http, (self.id, &self.token)).await
91    }
92
93    /// Edits the initial interaction response.
94    ///
95    /// **Note**: Message contents must be under 2000 unicode code points.
96    ///
97    /// # Errors
98    ///
99    /// Returns an [`Error::Model`] if the message content is too long. May also return an
100    /// [`Error::Http`] if the API returns an error, or an [`Error::Json`] if there is an error in
101    /// deserializing the API response.
102    pub async fn edit_response(
103        &self,
104        cache_http: impl CacheHttp,
105        builder: EditInteractionResponse,
106    ) -> Result<Message> {
107        builder.execute(cache_http, &self.token).await
108    }
109
110    /// Deletes the initial interaction response.
111    ///
112    /// Does not work on ephemeral messages.
113    ///
114    /// # Errors
115    ///
116    /// May return [`Error::Http`] if the API returns an error. Such as if the response was already
117    /// deleted.
118    pub async fn delete_response(&self, http: impl AsRef<Http>) -> Result<()> {
119        http.as_ref().delete_original_interaction_response(&self.token).await
120    }
121
122    /// Creates a followup response to the response sent.
123    ///
124    /// **Note**: Message contents must be under 2000 unicode code points.
125    ///
126    /// # Errors
127    ///
128    /// Returns [`Error::Model`] if the content is too long. May also return [`Error::Http`] if the
129    /// API returns an error, or [`Error::Json`] if there is an error in deserializing the
130    /// response.
131    pub async fn create_followup(
132        &self,
133        cache_http: impl CacheHttp,
134        builder: CreateInteractionResponseFollowup,
135    ) -> Result<Message> {
136        builder.execute(cache_http, (None, &self.token)).await
137    }
138
139    /// Edits a followup response to the response sent.
140    ///
141    /// **Note**: Message contents must be under 2000 unicode code points.
142    ///
143    /// # Errors
144    ///
145    /// Returns [`Error::Model`] if the content is too long. May also return [`Error::Http`] if the
146    /// API returns an error, or [`Error::Json`] if there is an error in deserializing the
147    /// response.
148    pub async fn edit_followup(
149        &self,
150        cache_http: impl CacheHttp,
151        message_id: impl Into<MessageId>,
152        builder: CreateInteractionResponseFollowup,
153    ) -> Result<Message> {
154        builder.execute(cache_http, (Some(message_id.into()), &self.token)).await
155    }
156
157    /// Deletes a followup message.
158    ///
159    /// # Errors
160    ///
161    /// May return [`Error::Http`] if the API returns an error. Such as if the response was already
162    /// deleted.
163    pub async fn delete_followup<M: Into<MessageId>>(
164        &self,
165        http: impl AsRef<Http>,
166        message_id: M,
167    ) -> Result<()> {
168        http.as_ref().delete_followup_message(&self.token, message_id.into()).await
169    }
170
171    /// Helper function to defer an interaction.
172    ///
173    /// # Errors
174    ///
175    /// Returns an [`Error::Http`] if the API returns an error, or an [`Error::Json`] if there is
176    /// an error in deserializing the API response.
177    pub async fn defer(&self, cache_http: impl CacheHttp) -> Result<()> {
178        self.create_response(cache_http, CreateInteractionResponse::Acknowledge).await
179    }
180
181    /// Helper function to defer an interaction ephemerally
182    ///
183    /// # Errors
184    ///
185    /// May also return an [`Error::Http`] if the API returns an error, or an [`Error::Json`] if
186    /// there is an error in deserializing the API response.
187    pub async fn defer_ephemeral(&self, cache_http: impl CacheHttp) -> Result<()> {
188        let builder = CreateInteractionResponse::Defer(
189            CreateInteractionResponseMessage::new().ephemeral(true),
190        );
191        self.create_response(cache_http, builder).await
192    }
193}
194
195// Manual impl needed to insert guild_id into resolved Role's
196impl<'de> Deserialize<'de> for ModalInteraction {
197    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
198        let mut interaction = Self::deserialize(deserializer)?; // calls #[serde(remote)]-generated inherent method
199        if let (Some(guild_id), Some(member)) = (interaction.guild_id, &mut interaction.member) {
200            member.guild_id = guild_id;
201            // If `member` is present, `user` wasn't sent and is still filled with default data
202            interaction.user = member.user.clone();
203        }
204        Ok(interaction)
205    }
206}
207
208impl Serialize for ModalInteraction {
209    fn serialize<S: serde::Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
210        Self::serialize(self, serializer) // calls #[serde(remote)]-generated inherent method
211    }
212}
213
214/// A modal submit interaction data, provided by [`ModalInteraction::data`]
215///
216/// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-data-structure).
217#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
218#[derive(Clone, Debug, Deserialize, Serialize)]
219#[non_exhaustive]
220pub struct ModalInteractionData {
221    /// The custom id of the modal
222    pub custom_id: String,
223    /// The components.
224    pub components: Vec<ActionRow>,
225}