use std::collections::HashMap;
use serde::de::Error as DeError;
use serde::{Deserialize, Deserializer};
#[cfg(feature = "http")]
use crate::builder::{
CreateInteractionResponse,
CreateInteractionResponseFollowup,
EditInteractionResponse,
};
#[cfg(feature = "http")]
use crate::http::Http;
use crate::internal::prelude::*;
#[cfg(feature = "http")]
use crate::json;
use crate::json::prelude::*;
use crate::model::application::command::{CommandOptionType, CommandType};
#[cfg(feature = "http")]
use crate::model::application::interaction::InteractionResponseType;
use crate::model::application::interaction::InteractionType;
use crate::model::channel::{Attachment, Message, PartialChannel};
use crate::model::guild::{Member, PartialMember, Role};
use crate::model::id::{
ApplicationId,
AttachmentId,
ChannelId,
CommandId,
GuildId,
InteractionId,
MessageId,
RoleId,
TargetId,
UserId,
};
use crate::model::user::User;
use crate::model::utils::deserialize_options_with_resolved;
use crate::model::Permissions;
#[derive(Clone, Debug, Serialize)]
#[non_exhaustive]
pub struct ApplicationCommandInteraction {
pub id: InteractionId,
pub application_id: ApplicationId,
#[serde(rename = "type")]
pub kind: InteractionType,
pub data: CommandData,
#[serde(skip_serializing_if = "Option::is_none")]
pub guild_id: Option<GuildId>,
pub channel_id: ChannelId,
#[serde(skip_serializing_if = "Option::is_none")]
pub member: Option<Member>,
pub user: User,
pub token: String,
pub version: u8,
pub app_permissions: Option<Permissions>,
pub locale: String,
pub guild_locale: Option<String>,
}
#[cfg(feature = "http")]
impl ApplicationCommandInteraction {
pub async fn get_interaction_response(&self, http: impl AsRef<Http>) -> Result<Message> {
http.as_ref().get_original_interaction_response(&self.token).await
}
pub async fn create_interaction_response<'a, F>(
&self,
http: impl AsRef<Http>,
f: F,
) -> Result<()>
where
for<'b> F:
FnOnce(&'b mut CreateInteractionResponse<'a>) -> &'b mut CreateInteractionResponse<'a>,
{
let mut interaction_response = CreateInteractionResponse::default();
f(&mut interaction_response);
self._create_interaction_response(http.as_ref(), interaction_response).await
}
async fn _create_interaction_response<'a>(
&self,
http: &Http,
interaction_response: CreateInteractionResponse<'a>,
) -> Result<()> {
let map = json::hashmap_to_json_map(interaction_response.0);
Message::check_lengths(&map)?;
if interaction_response.1.is_empty() {
http.as_ref()
.create_interaction_response(self.id.0, &self.token, &Value::from(map))
.await
} else {
http.as_ref()
.create_interaction_response_with_files(
self.id.0,
&self.token,
&Value::from(map),
interaction_response.1,
)
.await
}
}
pub async fn edit_original_interaction_response<F>(
&self,
http: impl AsRef<Http>,
f: F,
) -> Result<Message>
where
F: FnOnce(&mut EditInteractionResponse) -> &mut EditInteractionResponse,
{
let mut interaction_response = EditInteractionResponse::default();
f(&mut interaction_response);
let map = json::hashmap_to_json_map(interaction_response.0);
Message::check_lengths(&map)?;
http.as_ref().edit_original_interaction_response(&self.token, &Value::from(map)).await
}
pub async fn delete_original_interaction_response(&self, http: impl AsRef<Http>) -> Result<()> {
http.as_ref().delete_original_interaction_response(&self.token).await
}
pub async fn create_followup_message<'a, F>(
&self,
http: impl AsRef<Http>,
f: F,
) -> Result<Message>
where
for<'b> F: FnOnce(
&'b mut CreateInteractionResponseFollowup<'a>,
) -> &'b mut CreateInteractionResponseFollowup<'a>,
{
let mut interaction_response = CreateInteractionResponseFollowup::default();
f(&mut interaction_response);
self._create_followup_message(http.as_ref(), interaction_response).await
}
async fn _create_followup_message<'a>(
&self,
http: &Http,
interaction_response: CreateInteractionResponseFollowup<'a>,
) -> Result<Message> {
let map = json::hashmap_to_json_map(interaction_response.0);
Message::check_lengths(&map)?;
if interaction_response.1.is_empty() {
http.as_ref().create_followup_message(&self.token, &Value::from(map)).await
} else {
http.as_ref()
.create_followup_message_with_files(
&self.token,
&Value::from(map),
interaction_response.1,
)
.await
}
}
pub async fn edit_followup_message<'a, F, M: Into<MessageId>>(
&self,
http: impl AsRef<Http>,
message_id: M,
f: F,
) -> Result<Message>
where
for<'b> F: FnOnce(
&'b mut CreateInteractionResponseFollowup<'a>,
) -> &'b mut CreateInteractionResponseFollowup<'a>,
{
let mut interaction_response = CreateInteractionResponseFollowup::default();
f(&mut interaction_response);
let map = json::hashmap_to_json_map(interaction_response.0);
Message::check_lengths(&map)?;
let message_id = message_id.into().into();
if interaction_response.1.is_empty() {
http.as_ref().edit_followup_message(&self.token, message_id, &Value::from(map)).await
} else {
http.as_ref()
.edit_followup_message_and_attachments(
&self.token,
message_id,
&Value::from(map),
interaction_response.1,
)
.await
}
}
pub async fn delete_followup_message<M: Into<MessageId>>(
&self,
http: impl AsRef<Http>,
message_id: M,
) -> Result<()> {
http.as_ref().delete_followup_message(&self.token, message_id.into().into()).await
}
pub async fn get_followup_message<M: Into<MessageId>>(
&self,
http: impl AsRef<Http>,
message_id: M,
) -> Result<Message> {
http.as_ref().get_followup_message(&self.token, message_id.into().into()).await
}
pub async fn defer(&self, http: impl AsRef<Http>) -> Result<()> {
self.create_interaction_response(http, |f| {
f.kind(InteractionResponseType::DeferredChannelMessageWithSource)
})
.await
}
}
impl<'de> Deserialize<'de> for ApplicationCommandInteraction {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut map = JsonMap::deserialize(deserializer)?;
let id = map.get("guild_id").and_then(Value::as_str).and_then(|x| x.parse::<u64>().ok());
if let Some(guild_id) = id {
if let Some(member) = map.get_mut("member").and_then(Value::as_object_mut) {
member.insert("guild_id".to_string(), from_number(guild_id));
}
if let Some(data) = map.get_mut("data") {
if let Some(resolved) = data.get_mut("resolved") {
if let Some(roles) = resolved.get_mut("roles") {
if let Some(values) = roles.as_object_mut() {
for value in values.values_mut() {
value.as_object_mut().expect("couldn't deserialize").insert(
"guild_id".to_string(),
Value::from(guild_id.to_string()),
);
}
}
}
}
}
}
let id = map
.remove("id")
.ok_or_else(|| DeError::custom("expected id"))
.and_then(InteractionId::deserialize)
.map_err(DeError::custom)?;
let application_id = map
.remove("application_id")
.ok_or_else(|| DeError::custom("expected application id"))
.and_then(ApplicationId::deserialize)
.map_err(DeError::custom)?;
let kind = map
.remove("type")
.ok_or_else(|| DeError::custom("expected type"))
.and_then(InteractionType::deserialize)
.map_err(DeError::custom)?;
let data = map
.remove("data")
.ok_or_else(|| DeError::custom("expected data"))
.and_then(CommandData::deserialize)
.map_err(DeError::custom)?;
let guild_id = map
.remove("guild_id")
.map(GuildId::deserialize)
.transpose()
.map_err(DeError::custom)?;
let channel_id = map
.remove("channel_id")
.ok_or_else(|| DeError::custom("expected channel_id"))
.and_then(ChannelId::deserialize)
.map_err(DeError::custom)?;
let member =
map.remove("member").map(Member::deserialize).transpose().map_err(DeError::custom)?;
let user =
map.remove("user").map(User::deserialize).transpose().map_err(DeError::custom)?;
let user = user
.or_else(|| member.as_ref().map(|m| m.user.clone()))
.ok_or_else(|| DeError::custom("expected user or member"))?;
let token = map
.remove("token")
.ok_or_else(|| DeError::custom("expected token"))
.and_then(String::deserialize)
.map_err(DeError::custom)?;
let version = map
.remove("version")
.ok_or_else(|| DeError::custom("expected version"))
.and_then(u8::deserialize)
.map_err(DeError::custom)?;
let app_permissions = map
.remove("app_permissions")
.map(Permissions::deserialize)
.transpose()
.map_err(DeError::custom)?;
let guild_locale = map
.remove("guild_locale")
.map(String::deserialize)
.transpose()
.map_err(DeError::custom)?;
let locale = map
.remove("locale")
.ok_or_else(|| DeError::custom("expected locale"))
.and_then(String::deserialize)
.map_err(DeError::custom)?;
Ok(Self {
id,
application_id,
kind,
data,
guild_id,
channel_id,
member,
user,
token,
version,
app_permissions,
locale,
guild_locale,
})
}
}
#[derive(Clone, Debug, Serialize)]
#[non_exhaustive]
pub struct CommandData {
pub id: CommandId,
pub name: String,
#[serde(rename = "type")]
pub kind: CommandType,
#[serde(default)]
pub resolved: CommandDataResolved,
#[serde(default)]
pub options: Vec<CommandDataOption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub guild_id: Option<GuildId>,
pub target_id: Option<TargetId>,
}
impl CommandData {
#[must_use]
pub fn target(&self) -> Option<ResolvedTarget> {
match (self.kind, self.target_id) {
(CommandType::User, Some(id)) => {
let user_id = id.to_user_id();
let user = self.resolved.users.get(&user_id).cloned()?;
let member = self.resolved.members.get(&user_id).cloned();
Some(ResolvedTarget::User(user, member.map(Box::new)))
},
(CommandType::Message, Some(id)) => {
let message_id = id.to_message_id();
let message = self.resolved.messages.get(&message_id).cloned()?;
Some(ResolvedTarget::Message(Box::new(message)))
},
_ => None,
}
}
}
impl<'de> Deserialize<'de> for CommandData {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut map = JsonMap::deserialize(deserializer)?;
let name = map
.remove("name")
.ok_or_else(|| DeError::custom("expected value"))
.and_then(String::deserialize)
.map_err(DeError::custom)?;
let id = map
.remove("id")
.ok_or_else(|| DeError::custom("expected value"))
.and_then(CommandId::deserialize)
.map_err(DeError::custom)?;
let resolved = map
.remove("resolved")
.map(CommandDataResolved::deserialize)
.transpose()
.map_err(DeError::custom)?
.unwrap_or_default();
let options = map
.remove("options")
.map(|deserializer| deserialize_options_with_resolved(deserializer, &resolved))
.transpose()
.map_err(DeError::custom)?
.unwrap_or_default();
let kind = map
.remove("type")
.ok_or_else(|| DeError::custom("expected type"))
.and_then(CommandType::deserialize)
.map_err(DeError::custom)?;
let guild_id = match map.remove("guild_id") {
Some(id) => Option::<GuildId>::deserialize(id).map_err(DeError::custom)?,
None => None,
};
let target_id = match map.remove("target_id") {
Some(id) => Option::<TargetId>::deserialize(id).map_err(DeError::custom)?,
None => None,
};
Ok(Self {
id,
name,
kind,
resolved,
options,
guild_id,
target_id,
})
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
#[repr(u8)]
pub enum ResolvedTarget {
User(User, Option<Box<PartialMember>>),
Message(Box<Message>),
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[non_exhaustive]
pub struct CommandDataResolved {
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub users: HashMap<UserId, User>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub members: HashMap<UserId, PartialMember>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub roles: HashMap<RoleId, Role>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub channels: HashMap<ChannelId, PartialChannel>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub messages: HashMap<MessageId, Message>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub attachments: HashMap<AttachmentId, Attachment>,
}
#[derive(Clone, Debug, Serialize)]
#[non_exhaustive]
pub struct CommandDataOption {
pub name: String,
pub value: Option<Value>,
#[serde(rename = "type")]
pub kind: CommandOptionType,
#[serde(default)]
pub options: Vec<CommandDataOption>,
#[serde(default)]
pub resolved: Option<CommandDataOptionValue>,
#[serde(default)]
pub focused: bool,
}
impl<'de> Deserialize<'de> for CommandDataOption {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
let mut map = JsonMap::deserialize(deserializer)?;
let name = map
.remove("name")
.ok_or_else(|| DeError::custom("expected value"))
.and_then(String::deserialize)
.map_err(DeError::custom)?;
let value = map.remove("value");
let kind = map
.remove("type")
.ok_or_else(|| DeError::custom("expected type"))
.and_then(CommandOptionType::deserialize)
.map_err(DeError::custom)?;
let options = map
.remove("options")
.map(Vec::deserialize)
.transpose()
.map_err(DeError::custom)?
.unwrap_or_default();
let focused = match map.get("focused") {
Some(value) => value.as_bool().ok_or_else(|| DeError::custom("expected bool"))?,
None => false,
};
Ok(Self {
name,
value,
kind,
options,
resolved: None,
focused,
})
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
#[repr(u8)]
pub enum CommandDataOptionValue {
String(String),
Integer(i64),
Boolean(bool),
User(User, Option<PartialMember>),
Channel(PartialChannel),
Role(Role),
Number(f64),
Attachment(Attachment),
}
impl TargetId {
#[must_use]
pub fn to_user_id(self) -> UserId {
self.0.into()
}
#[must_use]
pub fn to_message_id(self) -> MessageId {
self.0.into()
}
}
impl From<MessageId> for TargetId {
fn from(id: MessageId) -> Self {
Self(id.0)
}
}
impl<'a> From<&'a MessageId> for TargetId {
fn from(id: &MessageId) -> Self {
Self(id.0)
}
}
impl From<UserId> for TargetId {
fn from(id: UserId) -> Self {
Self(id.0)
}
}
impl<'a> From<&'a UserId> for TargetId {
fn from(id: &UserId) -> Self {
Self(id.0)
}
}
impl From<TargetId> for MessageId {
fn from(id: TargetId) -> Self {
Self(id.0)
}
}
impl From<TargetId> for UserId {
fn from(id: TargetId) -> Self {
Self(id.0)
}
}