Skip to main content

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    /// Attachment size limit in bytes.
64    pub attachment_size_limit: u32,
65}
66
67#[cfg(feature = "model")]
68impl ModalInteraction {
69    /// Gets the interaction response.
70    ///
71    /// # Errors
72    ///
73    /// Returns an [`Error::Http`] if there is no interaction response.
74    pub async fn get_response(&self, http: impl AsRef<Http>) -> Result<Message> {
75        http.as_ref().get_original_interaction_response(&self.token).await
76    }
77
78    /// Creates a response to the interaction received.
79    ///
80    /// **Note**: Message contents must be under 2000 unicode code points.
81    ///
82    /// # Errors
83    ///
84    /// Returns an [`Error::Model`] if the message content is too long. May also return an
85    /// [`Error::Http`] if the API returns an error, or an [`Error::Json`] if there is an error in
86    /// deserializing the API response.
87    pub async fn create_response(
88        &self,
89        cache_http: impl CacheHttp,
90        builder: CreateInteractionResponse,
91    ) -> Result<()> {
92        builder.execute(cache_http, (self.id, &self.token)).await
93    }
94
95    /// Edits the initial interaction response.
96    ///
97    /// **Note**: Message contents must be under 2000 unicode code points.
98    ///
99    /// # Errors
100    ///
101    /// Returns an [`Error::Model`] if the message content is too long. May also return an
102    /// [`Error::Http`] if the API returns an error, or an [`Error::Json`] if there is an error in
103    /// deserializing the API response.
104    pub async fn edit_response(
105        &self,
106        cache_http: impl CacheHttp,
107        builder: EditInteractionResponse,
108    ) -> Result<Message> {
109        builder.execute(cache_http, &self.token).await
110    }
111
112    /// Deletes the initial interaction response.
113    ///
114    /// Does not work on ephemeral messages.
115    ///
116    /// # Errors
117    ///
118    /// May return [`Error::Http`] if the API returns an error. Such as if the response was already
119    /// deleted.
120    pub async fn delete_response(&self, http: impl AsRef<Http>) -> Result<()> {
121        http.as_ref().delete_original_interaction_response(&self.token).await
122    }
123
124    /// Creates a followup response to the response sent.
125    ///
126    /// **Note**: Message contents must be under 2000 unicode code points.
127    ///
128    /// # Errors
129    ///
130    /// Returns [`Error::Model`] if the content is too long. May also return [`Error::Http`] if the
131    /// API returns an error, or [`Error::Json`] if there is an error in deserializing the
132    /// response.
133    pub async fn create_followup(
134        &self,
135        cache_http: impl CacheHttp,
136        builder: CreateInteractionResponseFollowup,
137    ) -> Result<Message> {
138        builder.execute(cache_http, (None, &self.token)).await
139    }
140
141    /// Edits a followup response to the response sent.
142    ///
143    /// **Note**: Message contents must be under 2000 unicode code points.
144    ///
145    /// # Errors
146    ///
147    /// Returns [`Error::Model`] if the content is too long. May also return [`Error::Http`] if the
148    /// API returns an error, or [`Error::Json`] if there is an error in deserializing the
149    /// response.
150    pub async fn edit_followup(
151        &self,
152        cache_http: impl CacheHttp,
153        message_id: impl Into<MessageId>,
154        builder: CreateInteractionResponseFollowup,
155    ) -> Result<Message> {
156        builder.execute(cache_http, (Some(message_id.into()), &self.token)).await
157    }
158
159    /// Deletes a followup message.
160    ///
161    /// # Errors
162    ///
163    /// May return [`Error::Http`] if the API returns an error. Such as if the response was already
164    /// deleted.
165    pub async fn delete_followup<M: Into<MessageId>>(
166        &self,
167        http: impl AsRef<Http>,
168        message_id: M,
169    ) -> Result<()> {
170        http.as_ref().delete_followup_message(&self.token, message_id.into()).await
171    }
172
173    /// Helper function to defer an interaction.
174    ///
175    /// # Errors
176    ///
177    /// Returns an [`Error::Http`] if the API returns an error, or an [`Error::Json`] if there is
178    /// an error in deserializing the API response.
179    pub async fn defer(&self, cache_http: impl CacheHttp) -> Result<()> {
180        self.create_response(cache_http, CreateInteractionResponse::Acknowledge).await
181    }
182
183    /// Helper function to defer an interaction ephemerally
184    ///
185    /// # Errors
186    ///
187    /// May also return an [`Error::Http`] if the API returns an error, or an [`Error::Json`] if
188    /// there is an error in deserializing the API response.
189    pub async fn defer_ephemeral(&self, cache_http: impl CacheHttp) -> Result<()> {
190        let builder = CreateInteractionResponse::Defer(
191            CreateInteractionResponseMessage::new().ephemeral(true),
192        );
193        self.create_response(cache_http, builder).await
194    }
195}
196
197// Manual impl needed to insert guild_id into resolved Role's
198impl<'de> Deserialize<'de> for ModalInteraction {
199    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
200        let mut interaction = Self::deserialize(deserializer)?; // calls #[serde(remote)]-generated inherent method
201        if let (Some(guild_id), Some(member)) = (interaction.guild_id, &mut interaction.member) {
202            member.guild_id = guild_id;
203            // If `member` is present, `user` wasn't sent and is still filled with default data
204            interaction.user = member.user.clone();
205        }
206        Ok(interaction)
207    }
208}
209
210impl Serialize for ModalInteraction {
211    fn serialize<S: serde::Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
212        Self::serialize(self, serializer) // calls #[serde(remote)]-generated inherent method
213    }
214}
215
216/// A modal submit interaction data, provided by [`ModalInteraction::data`]
217///
218/// [Discord docs](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-data-structure).
219#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
220#[derive(Clone, Debug, Deserialize, Serialize)]
221#[non_exhaustive]
222pub struct ModalInteractionData {
223    /// The custom id of the modal
224    pub custom_id: String,
225    /// The components.
226    pub components: Vec<ActionRow>,
227}