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}