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}