1use serde::de::{Deserialize, Deserializer, Error as DeError};
2use serde::ser::{Serialize, Serializer};
3
4use super::{
5 CommandInteraction,
6 ComponentInteraction,
7 InstallationContext,
8 ModalInteraction,
9 PingInteraction,
10};
11use crate::internal::prelude::*;
12use crate::json::from_value;
13use crate::model::guild::PartialMember;
14use crate::model::id::{ApplicationId, GuildId, InteractionId, MessageId, UserId};
15use crate::model::monetization::Entitlement;
16use crate::model::user::User;
17use crate::model::utils::{deserialize_val, remove_from_map, StrOrInt};
18use crate::model::Permissions;
19
20#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
22#[derive(Clone, Debug)]
23#[non_exhaustive]
24#[allow(clippy::large_enum_variant)]
26pub enum Interaction {
27 Ping(PingInteraction),
28 Command(CommandInteraction),
29 Autocomplete(CommandInteraction),
30 Component(ComponentInteraction),
31 Modal(ModalInteraction),
32}
33
34impl Interaction {
35 #[must_use]
37 pub fn id(&self) -> InteractionId {
38 match self {
39 Self::Ping(i) => i.id,
40 Self::Command(i) | Self::Autocomplete(i) => i.id,
41 Self::Component(i) => i.id,
42 Self::Modal(i) => i.id,
43 }
44 }
45
46 #[must_use]
48 pub fn kind(&self) -> InteractionType {
49 match self {
50 Self::Ping(_) => InteractionType::Ping,
51 Self::Command(_) => InteractionType::Command,
52 Self::Component(_) => InteractionType::Component,
53 Self::Autocomplete(_) => InteractionType::Autocomplete,
54 Self::Modal(_) => InteractionType::Modal,
55 }
56 }
57
58 #[must_use]
60 pub fn app_permissions(&self) -> Option<Permissions> {
61 match self {
62 Self::Ping(_) => None,
63 Self::Command(i) | Self::Autocomplete(i) => i.app_permissions,
64 Self::Component(i) => i.app_permissions,
65 Self::Modal(i) => i.app_permissions,
66 }
67 }
68
69 #[must_use]
71 pub fn guild_id(&self) -> Option<GuildId> {
72 match self {
73 Self::Ping(_) => None,
74 Self::Command(i) | Self::Autocomplete(i) => i.guild_id,
75 Self::Component(i) => i.guild_id,
76 Self::Modal(i) => i.guild_id,
77 }
78 }
79
80 #[must_use]
82 pub fn application_id(&self) -> ApplicationId {
83 match self {
84 Self::Ping(i) => i.application_id,
85 Self::Command(i) | Self::Autocomplete(i) => i.application_id,
86 Self::Component(i) => i.application_id,
87 Self::Modal(i) => i.application_id,
88 }
89 }
90
91 #[must_use]
93 pub fn token(&self) -> &str {
94 match self {
95 Self::Ping(i) => i.token.as_str(),
96 Self::Command(i) | Self::Autocomplete(i) => i.token.as_str(),
97 Self::Component(i) => i.token.as_str(),
98 Self::Modal(i) => i.token.as_str(),
99 }
100 }
101
102 #[must_use]
104 pub fn guild_locale(&self) -> Option<&str> {
105 match self {
106 Self::Ping(_) => None,
107 Self::Command(i) | Self::Autocomplete(i) => i.guild_locale.as_deref(),
108 Self::Component(i) => i.guild_locale.as_deref(),
109 Self::Modal(i) => i.guild_locale.as_deref(),
110 }
111 }
112
113 #[must_use]
115 pub fn entitlements(&self) -> Option<&[Entitlement]> {
116 match self {
117 Self::Ping(_) => None,
118 Self::Command(i) | Self::Autocomplete(i) => Some(&i.entitlements),
119 Self::Component(i) => Some(&i.entitlements),
120 Self::Modal(i) => Some(&i.entitlements),
121 }
122 }
123
124 #[must_use]
126 pub fn ping(self) -> Option<PingInteraction> {
127 match self {
128 Self::Ping(i) => Some(i),
129 _ => None,
130 }
131 }
132
133 #[must_use]
135 pub fn as_ping(&self) -> Option<&PingInteraction> {
136 match self {
137 Self::Ping(i) => Some(i),
138 _ => None,
139 }
140 }
141
142 #[must_use]
144 pub fn into_ping(self) -> Option<PingInteraction> {
145 self.ping()
146 }
147
148 #[must_use]
150 pub fn command(self) -> Option<CommandInteraction> {
151 match self {
152 Self::Command(i) => Some(i),
153 _ => None,
154 }
155 }
156
157 #[must_use]
159 pub fn as_command(&self) -> Option<&CommandInteraction> {
160 match self {
161 Self::Command(i) => Some(i),
162 _ => None,
163 }
164 }
165
166 #[must_use]
168 pub fn into_command(self) -> Option<CommandInteraction> {
169 self.command()
170 }
171
172 #[must_use]
174 pub fn message_component(self) -> Option<ComponentInteraction> {
175 match self {
176 Self::Component(i) => Some(i),
177 _ => None,
178 }
179 }
180
181 #[must_use]
183 pub fn as_message_component(&self) -> Option<&ComponentInteraction> {
184 match self {
185 Self::Component(i) => Some(i),
186 _ => None,
187 }
188 }
189
190 #[must_use]
192 pub fn into_message_component(self) -> Option<ComponentInteraction> {
193 self.message_component()
194 }
195
196 #[must_use]
198 pub fn autocomplete(self) -> Option<CommandInteraction> {
199 match self {
200 Self::Autocomplete(i) => Some(i),
201 _ => None,
202 }
203 }
204
205 #[must_use]
207 pub fn as_autocomplete(&self) -> Option<&CommandInteraction> {
208 match self {
209 Self::Autocomplete(i) => Some(i),
210 _ => None,
211 }
212 }
213
214 #[must_use]
216 pub fn into_autocomplete(self) -> Option<CommandInteraction> {
217 self.autocomplete()
218 }
219
220 #[must_use]
222 pub fn modal_submit(self) -> Option<ModalInteraction> {
223 match self {
224 Self::Modal(i) => Some(i),
225 _ => None,
226 }
227 }
228
229 #[must_use]
231 pub fn as_modal_submit(&self) -> Option<&ModalInteraction> {
232 match self {
233 Self::Modal(i) => Some(i),
234 _ => None,
235 }
236 }
237
238 #[must_use]
240 pub fn into_modal_submit(self) -> Option<ModalInteraction> {
241 self.modal_submit()
242 }
243}
244
245impl<'de> Deserialize<'de> for Interaction {
247 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
248 let map = JsonMap::deserialize(deserializer)?;
249
250 let raw_kind = map.get("type").ok_or_else(|| DeError::missing_field("type"))?.clone();
251 let value = Value::from(map);
252
253 match deserialize_val(raw_kind)? {
254 InteractionType::Command => from_value(value).map(Interaction::Command),
255 InteractionType::Component => from_value(value).map(Interaction::Component),
256 InteractionType::Autocomplete => from_value(value).map(Interaction::Autocomplete),
257 InteractionType::Modal => from_value(value).map(Interaction::Modal),
258 InteractionType::Ping => from_value(value).map(Interaction::Ping),
259 InteractionType::Unknown(_) => return Err(DeError::custom("Unknown interaction type")),
260 }
261 .map_err(DeError::custom)
262 }
263}
264
265impl Serialize for Interaction {
266 fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
267 match self {
268 Self::Ping(i) => i.serialize(serializer),
269 Self::Command(i) | Self::Autocomplete(i) => i.serialize(serializer),
270 Self::Component(i) => i.serialize(serializer),
271 Self::Modal(i) => i.serialize(serializer),
272 }
273 }
274}
275
276enum_number! {
277 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
281 #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
282 #[serde(from = "u8", into = "u8")]
283 #[non_exhaustive]
284 pub enum InteractionType {
285 Ping = 1,
286 Command = 2,
287 Component = 3,
288 Autocomplete = 4,
289 Modal = 5,
290 _ => Unknown(u8),
291 }
292}
293
294bitflags! {
295 #[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)]
300 pub struct InteractionResponseFlags: u64 {
301 const SUPPRESS_EMBEDS = 1 << 2;
303 const EPHEMERAL = 1 << 6;
306 const SUPPRESS_NOTIFICATIONS = 1 << 12;
308 }
309}
310
311#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
315#[derive(Clone, Debug)]
316#[non_exhaustive]
317pub enum AuthorizingIntegrationOwner {
318 GuildInstall(Option<GuildId>),
322 UserInstall(UserId),
326 Unknown(InstallationContext),
327}
328
329#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
330#[derive(Clone, Debug, Default)]
331#[repr(transparent)]
332pub struct AuthorizingIntegrationOwners(pub Vec<AuthorizingIntegrationOwner>);
333
334impl<'de> serde::Deserialize<'de> for AuthorizingIntegrationOwners {
335 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
336 struct Visitor;
337
338 impl<'de> serde::de::Visitor<'de> for Visitor {
339 type Value = AuthorizingIntegrationOwners;
340
341 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
342 formatter.write_str("a hashmap containing keys of InstallationContext and values based on those keys")
343 }
344
345 fn visit_map<A>(self, mut map: A) -> StdResult<Self::Value, A::Error>
346 where
347 A: serde::de::MapAccess<'de>,
348 {
349 let mut out = Vec::new();
350 while let Some(key_str) = map.next_key::<serde_cow::CowStr<'_>>()? {
351 let key_int = key_str.0.parse::<u8>().map_err(serde::de::Error::custom)?;
352 let value = match InstallationContext::from(key_int) {
353 InstallationContext::Guild => {
354 let id_str = map.next_value::<StrOrInt<'_>>()?;
357 let id_int = id_str.parse().map_err(A::Error::custom)?;
358 let id = std::num::NonZeroU64::new(id_int).map(GuildId::from);
359
360 AuthorizingIntegrationOwner::GuildInstall(id)
361 },
362 InstallationContext::User => {
363 AuthorizingIntegrationOwner::UserInstall(map.next_value()?)
364 },
365 key => AuthorizingIntegrationOwner::Unknown(key),
366 };
367
368 out.push(value);
369 }
370
371 Ok(AuthorizingIntegrationOwners(out))
372 }
373 }
374
375 deserializer.deserialize_map(Visitor)
376 }
377}
378
379impl serde::Serialize for AuthorizingIntegrationOwners {
380 fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
381 use serde::ser::SerializeMap;
382
383 let mut serializer = serializer.serialize_map(Some(self.0.len()))?;
384 for value in &self.0 {
385 match value {
386 AuthorizingIntegrationOwner::GuildInstall(inner) => {
387 serializer.serialize_entry(&InstallationContext::Guild, &inner)
388 },
389 AuthorizingIntegrationOwner::UserInstall(inner) => {
390 serializer.serialize_entry(&InstallationContext::User, &inner)
391 },
392 AuthorizingIntegrationOwner::Unknown(inner) => {
393 serializer.serialize_entry(&inner, &())
394 },
395 }?;
396 }
397
398 serializer.end()
399 }
400}
401
402#[cfg_attr(
408 all(not(ignore_serenity_deprecated), feature = "unstable_discord_api"),
409 deprecated = "Use Message::interaction_metadata"
410)]
411#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
412#[derive(Clone, Debug, Deserialize, Serialize)]
413#[non_exhaustive]
414pub struct MessageInteraction {
415 pub id: InteractionId,
417 #[serde(rename = "type")]
419 pub kind: InteractionType,
420 pub name: String,
424 pub user: User,
426 #[serde(skip_serializing_if = "Option::is_none")]
428 pub member: Option<PartialMember>,
429}
430
431#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
432#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
433#[non_exhaustive]
434pub struct MessageCommandInteractionMetadata {
435 pub id: InteractionId,
437 pub user: User,
439 pub authorizing_integration_owners: AuthorizingIntegrationOwners,
441 pub original_response_message_id: Option<MessageId>,
443 pub target_user: Option<User>,
445 pub target_message_id: Option<MessageId>,
448}
449
450#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
451#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
452#[non_exhaustive]
453pub struct MessageComponentInteractionMetadata {
454 pub id: InteractionId,
456 pub user: User,
458 pub authorizing_integration_owners: AuthorizingIntegrationOwners,
460 pub original_response_message_id: Option<MessageId>,
462 pub interacted_message_id: MessageId,
464}
465
466#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
467#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
468#[non_exhaustive]
469pub struct MessageModalSubmitInteractionMetadata {
470 pub id: InteractionId,
472 pub user: User,
474 pub authorizing_integration_owners: AuthorizingIntegrationOwners,
476 pub original_response_message_id: Option<MessageId>,
478 pub triggering_interaction_metadata: Box<MessageInteractionMetadata>,
480}
481
482#[allow(clippy::large_enum_variant)]
485#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
486#[derive(Clone, Debug)]
487#[non_exhaustive]
488pub enum MessageInteractionMetadata {
489 Command(MessageCommandInteractionMetadata),
490 Component(MessageComponentInteractionMetadata),
491 ModalSubmit(MessageModalSubmitInteractionMetadata),
492 Unknown(InteractionType),
493}
494
495impl<'de> serde::Deserialize<'de> for MessageInteractionMetadata {
496 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
497 let mut data = JsonMap::deserialize(deserializer)?;
498 let kind: InteractionType = remove_from_map(&mut data, "type")?;
499
500 match kind {
501 InteractionType::Command => deserialize_val(Value::from(data)).map(Self::Command),
502 InteractionType::Component => deserialize_val(Value::from(data)).map(Self::Component),
503 InteractionType::Modal => deserialize_val(Value::from(data)).map(Self::ModalSubmit),
504
505 unknown => Ok(Self::Unknown(unknown)),
506 }
507 }
508}
509
510impl serde::Serialize for MessageInteractionMetadata {
511 fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
512 #[derive(serde::Serialize)]
513 struct WithType<T> {
514 #[serde(rename = "type")]
515 kind: InteractionType,
516 #[serde(flatten)]
517 val: T,
518 }
519
520 fn serialize_with_type<S: Serializer, T: serde::Serialize>(
521 serializer: S,
522 val: T,
523 kind: InteractionType,
524 ) -> StdResult<S::Ok, S::Error> {
525 let wrapper = WithType {
526 kind,
527 val,
528 };
529
530 wrapper.serialize(serializer)
531 }
532
533 match self {
534 MessageInteractionMetadata::Command(val) => {
535 serialize_with_type(serializer, val, InteractionType::Command)
536 },
537 MessageInteractionMetadata::Component(val) => {
538 serialize_with_type(serializer, val, InteractionType::Component)
539 },
540 MessageInteractionMetadata::ModalSubmit(val) => {
541 serialize_with_type(serializer, val, InteractionType::Modal)
542 },
543 &MessageInteractionMetadata::Unknown(kind) => {
544 tracing::warn!("Tried to serialize MessageInteractionMetadata::Unknown({}), serialising null instead", u8::from(kind));
545 serializer.serialize_none()
546 },
547 }
548 }
549}