use crate::api::BotApi;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum InteractionType {
Ping = 1,
ApplicationCommand = 2,
HttpProxy = 10,
InlineKeyboard = 11,
}
impl From<u8> for InteractionType {
fn from(value: u8) -> Self {
match value {
1 => Self::Ping,
2 => Self::ApplicationCommand,
10 => Self::HttpProxy,
11 => Self::InlineKeyboard,
_ => Self::Ping, }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u8)]
pub enum InteractionDataType {
ChatInputSearch = 9,
HttpProxy = 10,
InlineKeyboardButtonClick = 11,
}
impl From<u8> for InteractionDataType {
fn from(value: u8) -> Self {
match value {
9 => Self::ChatInputSearch,
10 => Self::HttpProxy,
11 => Self::InlineKeyboardButtonClick,
_ => Self::ChatInputSearch, }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Resolved {
pub button_id: Option<String>,
pub button_data: Option<String>,
pub message_id: Option<String>,
pub user_id: Option<String>,
pub feature_id: Option<String>,
}
impl Resolved {
pub fn new(data: &Value) -> Self {
Self {
button_id: data
.get("button_id")
.and_then(|v| v.as_str())
.map(String::from),
button_data: data
.get("button_data")
.and_then(|v| v.as_str())
.map(String::from),
message_id: data
.get("message_id")
.and_then(|v| v.as_str())
.map(String::from),
user_id: data
.get("user_id")
.and_then(|v| v.as_str())
.map(String::from),
feature_id: data
.get("feature_id")
.and_then(|v| v.as_str())
.map(String::from),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InteractionData {
pub data_type: Option<InteractionDataType>,
pub resolved: Resolved,
}
impl InteractionData {
pub fn new(data: &Value) -> Self {
Self {
data_type: data
.get("type")
.and_then(|v| v.as_u64())
.map(|v| InteractionDataType::from(v as u8)),
resolved: Resolved::new(
data.get("resolved")
.unwrap_or(&Value::Object(serde_json::Map::new())),
),
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct Interaction {
#[serde(skip)]
api: BotApi,
pub id: Option<String>,
pub application_id: Option<u64>,
pub interaction_type: Option<InteractionType>,
pub scene: Option<String>,
pub chat_type: Option<u64>,
pub event_id: Option<String>,
pub data: InteractionData,
pub guild_id: Option<String>,
pub channel_id: Option<String>,
pub user_openid: Option<String>,
pub group_openid: Option<String>,
pub group_member_openid: Option<String>,
pub timestamp: Option<u64>,
pub version: Option<u64>,
}
impl Interaction {
pub fn new(api: BotApi, event_id: Option<String>, data: &Value) -> Self {
Self {
api,
event_id,
id: data.get("id").and_then(|v| v.as_str()).map(String::from),
application_id: data.get("application_id").and_then(|v| v.as_u64()),
interaction_type: data
.get("type")
.and_then(|v| v.as_u64())
.map(|v| InteractionType::from(v as u8)),
scene: data.get("scene").and_then(|v| v.as_str()).map(String::from),
chat_type: data.get("chat_type").and_then(|v| v.as_u64()),
data: InteractionData::new(
data.get("data")
.unwrap_or(&Value::Object(serde_json::Map::new())),
),
guild_id: data
.get("guild_id")
.and_then(|v| v.as_str())
.map(String::from),
channel_id: data
.get("channel_id")
.and_then(|v| v.as_str())
.map(String::from),
user_openid: data
.get("user_openid")
.and_then(|v| v.as_str())
.map(String::from),
group_openid: data
.get("group_openid")
.and_then(|v| v.as_str())
.map(String::from),
group_member_openid: data
.get("group_member_openid")
.and_then(|v| v.as_str())
.map(String::from),
timestamp: data.get("timestamp").and_then(|v| v.as_u64()),
version: data.get("version").and_then(|v| v.as_u64()),
}
}
pub fn api(&self) -> &BotApi {
&self.api
}
pub fn is_button_interaction(&self) -> bool {
matches!(
self.data.data_type,
Some(InteractionDataType::InlineKeyboardButtonClick)
)
}
pub fn is_command_interaction(&self) -> bool {
matches!(
self.interaction_type,
Some(InteractionType::ApplicationCommand)
)
}
pub fn button_id(&self) -> Option<&str> {
self.data.resolved.button_id.as_deref()
}
pub fn button_data(&self) -> Option<&str> {
self.data.resolved.button_data.as_deref()
}
}
impl std::fmt::Display for Interaction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Interaction {{ id: {:?}, type: {:?}, scene: {:?}, chat_type: {:?}, event_id: {:?} }}",
self.id, self.interaction_type, self.scene, self.chat_type, self.event_id
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_interaction_type() {
assert_eq!(InteractionType::Ping as u8, 1);
assert_eq!(InteractionType::ApplicationCommand as u8, 2);
assert_eq!(InteractionType::HttpProxy as u8, 10);
assert_eq!(InteractionType::InlineKeyboard as u8, 11);
}
#[test]
fn test_interaction_data_type() {
assert_eq!(InteractionDataType::ChatInputSearch as u8, 9);
assert_eq!(InteractionDataType::HttpProxy as u8, 10);
assert_eq!(InteractionDataType::InlineKeyboardButtonClick as u8, 11);
}
#[test]
fn test_interaction_type_from() {
assert_eq!(InteractionType::from(1), InteractionType::Ping);
assert_eq!(
InteractionType::from(2),
InteractionType::ApplicationCommand
);
assert_eq!(InteractionType::from(10), InteractionType::HttpProxy);
assert_eq!(InteractionType::from(11), InteractionType::InlineKeyboard);
}
#[test]
fn test_interaction_data_type_from() {
assert_eq!(
InteractionDataType::from(9),
InteractionDataType::ChatInputSearch
);
assert_eq!(
InteractionDataType::from(10),
InteractionDataType::HttpProxy
);
assert_eq!(
InteractionDataType::from(11),
InteractionDataType::InlineKeyboardButtonClick
);
}
}