use serde::de::{Deserialize, Deserializer, Error as DeError};
use serde::ser::{Serialize, Serializer};
use super::{
CommandInteraction,
ComponentInteraction,
InstallationContext,
ModalInteraction,
PingInteraction,
};
use crate::internal::prelude::*;
use crate::json::from_value;
use crate::model::guild::PartialMember;
use crate::model::id::{ApplicationId, GuildId, InteractionId, MessageId, UserId};
use crate::model::monetization::Entitlement;
use crate::model::user::User;
use crate::model::utils::{deserialize_val, remove_from_map, StrOrInt};
use crate::model::Permissions;
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
#[derive(Clone, Debug)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
pub enum Interaction {
Ping(PingInteraction),
Command(CommandInteraction),
Autocomplete(CommandInteraction),
Component(ComponentInteraction),
Modal(ModalInteraction),
}
impl Interaction {
#[must_use]
pub fn id(&self) -> InteractionId {
match self {
Self::Ping(i) => i.id,
Self::Command(i) | Self::Autocomplete(i) => i.id,
Self::Component(i) => i.id,
Self::Modal(i) => i.id,
}
}
#[must_use]
pub fn kind(&self) -> InteractionType {
match self {
Self::Ping(_) => InteractionType::Ping,
Self::Command(_) => InteractionType::Command,
Self::Component(_) => InteractionType::Component,
Self::Autocomplete(_) => InteractionType::Autocomplete,
Self::Modal(_) => InteractionType::Modal,
}
}
#[must_use]
pub fn app_permissions(&self) -> Option<Permissions> {
match self {
Self::Ping(_) => None,
Self::Command(i) | Self::Autocomplete(i) => i.app_permissions,
Self::Component(i) => i.app_permissions,
Self::Modal(i) => i.app_permissions,
}
}
#[must_use]
pub fn guild_id(&self) -> Option<GuildId> {
match self {
Self::Ping(_) => None,
Self::Command(i) | Self::Autocomplete(i) => i.guild_id,
Self::Component(i) => i.guild_id,
Self::Modal(i) => i.guild_id,
}
}
#[must_use]
pub fn application_id(&self) -> ApplicationId {
match self {
Self::Ping(i) => i.application_id,
Self::Command(i) | Self::Autocomplete(i) => i.application_id,
Self::Component(i) => i.application_id,
Self::Modal(i) => i.application_id,
}
}
#[must_use]
pub fn token(&self) -> &str {
match self {
Self::Ping(i) => i.token.as_str(),
Self::Command(i) | Self::Autocomplete(i) => i.token.as_str(),
Self::Component(i) => i.token.as_str(),
Self::Modal(i) => i.token.as_str(),
}
}
#[must_use]
pub fn guild_locale(&self) -> Option<&str> {
match self {
Self::Ping(_) => None,
Self::Command(i) | Self::Autocomplete(i) => i.guild_locale.as_deref(),
Self::Component(i) => i.guild_locale.as_deref(),
Self::Modal(i) => i.guild_locale.as_deref(),
}
}
#[must_use]
pub fn entitlements(&self) -> Option<&[Entitlement]> {
match self {
Self::Ping(_) => None,
Self::Command(i) | Self::Autocomplete(i) => Some(&i.entitlements),
Self::Component(i) => Some(&i.entitlements),
Self::Modal(i) => Some(&i.entitlements),
}
}
#[must_use]
pub fn ping(self) -> Option<PingInteraction> {
match self {
Self::Ping(i) => Some(i),
_ => None,
}
}
#[must_use]
pub fn as_ping(&self) -> Option<&PingInteraction> {
match self {
Self::Ping(i) => Some(i),
_ => None,
}
}
#[must_use]
pub fn into_ping(self) -> Option<PingInteraction> {
self.ping()
}
#[must_use]
pub fn command(self) -> Option<CommandInteraction> {
match self {
Self::Command(i) => Some(i),
_ => None,
}
}
#[must_use]
pub fn as_command(&self) -> Option<&CommandInteraction> {
match self {
Self::Command(i) => Some(i),
_ => None,
}
}
#[must_use]
pub fn into_command(self) -> Option<CommandInteraction> {
self.command()
}
#[must_use]
pub fn message_component(self) -> Option<ComponentInteraction> {
match self {
Self::Component(i) => Some(i),
_ => None,
}
}
#[must_use]
pub fn as_message_component(&self) -> Option<&ComponentInteraction> {
match self {
Self::Component(i) => Some(i),
_ => None,
}
}
#[must_use]
pub fn into_message_component(self) -> Option<ComponentInteraction> {
self.message_component()
}
#[must_use]
pub fn autocomplete(self) -> Option<CommandInteraction> {
match self {
Self::Autocomplete(i) => Some(i),
_ => None,
}
}
#[must_use]
pub fn as_autocomplete(&self) -> Option<&CommandInteraction> {
match self {
Self::Autocomplete(i) => Some(i),
_ => None,
}
}
#[must_use]
pub fn into_autocomplete(self) -> Option<CommandInteraction> {
self.autocomplete()
}
#[must_use]
pub fn modal_submit(self) -> Option<ModalInteraction> {
match self {
Self::Modal(i) => Some(i),
_ => None,
}
}
#[must_use]
pub fn as_modal_submit(&self) -> Option<&ModalInteraction> {
match self {
Self::Modal(i) => Some(i),
_ => None,
}
}
#[must_use]
pub fn into_modal_submit(self) -> Option<ModalInteraction> {
self.modal_submit()
}
}
impl<'de> Deserialize<'de> for Interaction {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> std::result::Result<Self, D::Error> {
let map = JsonMap::deserialize(deserializer)?;
let raw_kind = map.get("type").ok_or_else(|| DeError::missing_field("type"))?.clone();
let value = Value::from(map);
match deserialize_val(raw_kind)? {
InteractionType::Command => from_value(value).map(Interaction::Command),
InteractionType::Component => from_value(value).map(Interaction::Component),
InteractionType::Autocomplete => from_value(value).map(Interaction::Autocomplete),
InteractionType::Modal => from_value(value).map(Interaction::Modal),
InteractionType::Ping => from_value(value).map(Interaction::Ping),
InteractionType::Unknown(_) => return Err(DeError::custom("Unknown interaction type")),
}
.map_err(DeError::custom)
}
}
impl Serialize for Interaction {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
match self {
Self::Ping(i) => i.serialize(serializer),
Self::Command(i) | Self::Autocomplete(i) => i.serialize(serializer),
Self::Component(i) => i.serialize(serializer),
Self::Modal(i) => i.serialize(serializer),
}
}
}
enum_number! {
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
#[serde(from = "u8", into = "u8")]
#[non_exhaustive]
pub enum InteractionType {
Ping = 1,
Command = 2,
Component = 3,
Autocomplete = 4,
Modal = 5,
_ => Unknown(u8),
}
}
bitflags! {
#[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)]
pub struct InteractionResponseFlags: u64 {
const SUPPRESS_EMBEDS = 1 << 2;
const EPHEMERAL = 1 << 6;
const SUPPRESS_NOTIFICATIONS = 1 << 12;
}
}
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum AuthorizingIntegrationOwner {
GuildInstall(Option<GuildId>),
UserInstall(UserId),
Unknown(InstallationContext),
}
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
#[derive(Clone, Debug, Default)]
#[repr(transparent)]
pub struct AuthorizingIntegrationOwners(pub Vec<AuthorizingIntegrationOwner>);
impl<'de> serde::Deserialize<'de> for AuthorizingIntegrationOwners {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = AuthorizingIntegrationOwners;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a hashmap containing keys of InstallationContext and values based on those keys")
}
fn visit_map<A>(self, mut map: A) -> StdResult<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut out = Vec::new();
while let Some(key_str) = map.next_key::<serde_cow::CowStr<'_>>()? {
let key_int = key_str.0.parse::<u8>().map_err(serde::de::Error::custom)?;
let value = match InstallationContext::from(key_int) {
InstallationContext::Guild => {
let id_str = map.next_value::<StrOrInt<'_>>()?;
let id_int = id_str.parse().map_err(A::Error::custom)?;
let id = std::num::NonZeroU64::new(id_int).map(GuildId::from);
AuthorizingIntegrationOwner::GuildInstall(id)
},
InstallationContext::User => {
AuthorizingIntegrationOwner::UserInstall(map.next_value()?)
},
key => AuthorizingIntegrationOwner::Unknown(key),
};
out.push(value);
}
Ok(AuthorizingIntegrationOwners(out))
}
}
deserializer.deserialize_map(Visitor)
}
}
impl serde::Serialize for AuthorizingIntegrationOwners {
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
use serde::ser::SerializeMap;
let mut serializer = serializer.serialize_map(Some(self.0.len()))?;
for value in &self.0 {
match value {
AuthorizingIntegrationOwner::GuildInstall(inner) => {
serializer.serialize_entry(&InstallationContext::Guild, &inner)
},
AuthorizingIntegrationOwner::UserInstall(inner) => {
serializer.serialize_entry(&InstallationContext::User, &inner)
},
AuthorizingIntegrationOwner::Unknown(inner) => {
serializer.serialize_entry(&inner, &())
},
}?;
}
serializer.end()
}
}
#[cfg_attr(
all(not(ignore_serenity_deprecated), feature = "unstable_discord_api"),
deprecated = "Use Message::interaction_metadata"
)]
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
#[derive(Clone, Debug, Deserialize, Serialize)]
#[non_exhaustive]
pub struct MessageInteraction {
pub id: InteractionId,
#[serde(rename = "type")]
pub kind: InteractionType,
pub name: String,
pub user: User,
#[serde(skip_serializing_if = "Option::is_none")]
pub member: Option<PartialMember>,
}
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[non_exhaustive]
pub struct MessageCommandInteractionMetadata {
pub id: InteractionId,
pub user: User,
pub authorizing_integration_owners: AuthorizingIntegrationOwners,
pub original_response_message_id: Option<MessageId>,
pub target_user: Option<User>,
pub target_message_id: Option<MessageId>,
}
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[non_exhaustive]
pub struct MessageComponentInteractionMetadata {
pub id: InteractionId,
pub user: User,
pub authorizing_integration_owners: AuthorizingIntegrationOwners,
pub original_response_message_id: Option<MessageId>,
pub interacted_message_id: MessageId,
}
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[non_exhaustive]
pub struct MessageModalSubmitInteractionMetadata {
pub id: InteractionId,
pub user: User,
pub authorizing_integration_owners: AuthorizingIntegrationOwners,
pub original_response_message_id: Option<MessageId>,
pub triggering_interaction_metadata: Box<MessageInteractionMetadata>,
}
#[allow(clippy::large_enum_variant)]
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum MessageInteractionMetadata {
Command(MessageCommandInteractionMetadata),
Component(MessageComponentInteractionMetadata),
ModalSubmit(MessageModalSubmitInteractionMetadata),
Unknown(InteractionType),
}
impl<'de> serde::Deserialize<'de> for MessageInteractionMetadata {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut data = JsonMap::deserialize(deserializer)?;
let kind: InteractionType = remove_from_map(&mut data, "type")?;
match kind {
InteractionType::Command => deserialize_val(Value::from(data)).map(Self::Command),
InteractionType::Component => deserialize_val(Value::from(data)).map(Self::Component),
InteractionType::Modal => deserialize_val(Value::from(data)).map(Self::ModalSubmit),
unknown => Ok(Self::Unknown(unknown)),
}
}
}
impl serde::Serialize for MessageInteractionMetadata {
fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
#[derive(serde::Serialize)]
struct WithType<T> {
#[serde(rename = "type")]
kind: InteractionType,
#[serde(flatten)]
val: T,
}
fn serialize_with_type<S: Serializer, T: serde::Serialize>(
serializer: S,
val: T,
kind: InteractionType,
) -> StdResult<S::Ok, S::Error> {
let wrapper = WithType {
kind,
val,
};
wrapper.serialize(serializer)
}
match self {
MessageInteractionMetadata::Command(val) => {
serialize_with_type(serializer, val, InteractionType::Command)
},
MessageInteractionMetadata::Component(val) => {
serialize_with_type(serializer, val, InteractionType::Component)
},
MessageInteractionMetadata::ModalSubmit(val) => {
serialize_with_type(serializer, val, InteractionType::Modal)
},
&MessageInteractionMetadata::Unknown(kind) => {
tracing::warn!("Tried to serialize MessageInteractionMetadata::Unknown({}), serialising null instead", u8::from(kind));
serializer.serialize_none()
},
}
}
}