use crate::models::{HasId, Snowflake, Timestamp};
use base64::Engine;
use serde::{Deserialize, Serialize};
use serde_json::Value;
fn is_zero_u32(value: &u32) -> bool {
*value == 0
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct CMD {
pub cmd: String,
pub content: String,
}
pub fn mention_user(user_id: impl std::fmt::Display) -> String {
format!("<@{user_id}>")
}
pub fn mention_all_user() -> &'static str {
"@everyone"
}
pub fn mention_channel(channel_id: impl std::fmt::Display) -> String {
format!("<#{channel_id}>")
}
pub fn emoji(id: impl std::fmt::Display) -> String {
format!("<emoji:{id}>")
}
pub fn parse_command(input: &str) -> CMD {
let cleaned = input
.split_whitespace()
.filter(|part| !(part.starts_with("<@!") && part.ends_with('>')))
.collect::<Vec<_>>()
.join(" ");
let mut parts = cleaned.splitn(2, char::is_whitespace);
CMD {
cmd: parts.next().unwrap_or_default().trim().to_string(),
content: parts.next().unwrap_or_default().trim().to_string(),
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct MessageScene {
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub callback_data: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MessageDelete {
pub message: Message,
pub op_user: crate::models::User,
#[serde(skip)]
pub event_id: Option<String>,
}
impl MessageDelete {
pub fn from_data(api: crate::api::BotApi, event_id: String, data: serde_json::Value) -> Self {
let message_data = data.get("message").cloned().unwrap_or_else(|| data.clone());
let op_user = data
.get("op_user")
.cloned()
.map(crate::models::User::from_data)
.unwrap_or_default();
Self {
message: Message::from_data(api, event_id.clone(), message_data),
op_user,
event_id: Some(event_id),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Message {
pub id: Option<Snowflake>,
pub content: Option<String>,
pub channel_id: Option<Snowflake>,
pub guild_id: Option<Snowflake>,
pub group_id: Option<Snowflake>,
pub author: Option<MessageUser>,
pub member: Option<MessageMember>,
pub message_reference: Option<MessageReference>,
#[serde(default)]
pub mentions: Vec<MessageUser>,
#[serde(default)]
pub attachments: Vec<MessageAttachment>,
#[serde(default)]
pub embeds: Vec<Embed>,
pub ark: Option<Ark>,
pub direct_message: Option<bool>,
pub seq: Option<u64>,
pub seq_in_channel: Option<String>,
pub timestamp: Option<Timestamp>,
pub edited_timestamp: Option<Timestamp>,
pub mention_everyone: Option<bool>,
pub src_guild_id: Option<Snowflake>,
pub file_info: Option<String>,
pub ttl: Option<u32>,
pub message_scene: Option<MessageScene>,
pub event_id: Option<String>,
}
impl Message {
pub fn new() -> Self {
Self {
id: None,
content: None,
channel_id: None,
guild_id: None,
group_id: None,
author: None,
member: None,
message_reference: None,
mentions: Vec::new(),
attachments: Vec::new(),
embeds: Vec::new(),
ark: None,
direct_message: None,
seq: None,
seq_in_channel: None,
timestamp: None,
edited_timestamp: None,
mention_everyone: None,
src_guild_id: None,
file_info: None,
ttl: None,
message_scene: None,
event_id: None,
}
}
pub fn from_data(_api: crate::api::BotApi, event_id: String, data: serde_json::Value) -> Self {
Self {
id: data.get("id").and_then(|v| v.as_str()).map(String::from),
content: data
.get("content")
.and_then(|v| v.as_str())
.map(String::from),
channel_id: data
.get("channel_id")
.and_then(|v| v.as_str())
.map(String::from),
guild_id: data
.get("guild_id")
.and_then(|v| v.as_str())
.map(String::from),
group_id: data
.get("group_id")
.and_then(|v| v.as_str())
.map(String::from),
author: data
.get("author")
.map(|v| MessageUser::from_data(v.clone())),
member: data.get("member").map(|v| MessageMember {
nick: v.get("nick").and_then(|n| n.as_str()).map(String::from),
roles: v.get("roles").and_then(|r| r.as_array()).map(|arr| {
arr.iter()
.filter_map(|v| v.as_str())
.map(String::from)
.collect()
}),
joined_at: v
.get("joined_at")
.and_then(|j| j.as_str())
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
.map(|dt| dt.with_timezone(&chrono::Utc)),
}),
message_reference: data.get("message_reference").map(|v| MessageReference {
message_id: v
.get("message_id")
.and_then(|id| id.as_str())
.map(String::from),
}),
mentions: data
.get("mentions")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|v| MessageUser::from_data(v.clone()))
.collect()
})
.unwrap_or_default(),
attachments: data
.get("attachments")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|v| MessageAttachment::from_data(v.clone()))
.collect()
})
.unwrap_or_default(),
embeds: data
.get("embeds")
.cloned()
.and_then(|v| serde_json::from_value(v).ok())
.unwrap_or_default(),
ark: data
.get("ark")
.cloned()
.and_then(|v| serde_json::from_value(v).ok()),
direct_message: data.get("direct_message").and_then(|v| v.as_bool()),
seq: data.get("seq").and_then(|v| v.as_u64()),
seq_in_channel: data
.get("seq_in_channel")
.and_then(|v| v.as_str())
.map(String::from),
timestamp: data
.get("timestamp")
.and_then(|v| v.as_str())
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
.map(|dt| dt.with_timezone(&chrono::Utc)),
edited_timestamp: data
.get("edited_timestamp")
.and_then(|v| v.as_str())
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
.map(|dt| dt.with_timezone(&chrono::Utc)),
mention_everyone: data.get("mention_everyone").and_then(|v| v.as_bool()),
src_guild_id: data
.get("src_guild_id")
.and_then(|v| v.as_str())
.map(String::from),
file_info: data
.get("file_info")
.and_then(|v| v.as_str())
.map(String::from),
ttl: data.get("ttl").and_then(|v| v.as_u64()).map(|v| v as u32),
message_scene: data
.get("message_scene")
.cloned()
.and_then(|v| serde_json::from_value(v).ok()),
event_id: Some(event_id),
}
}
pub async fn reply(
&self,
api: &crate::api::BotApi,
token: &crate::token::Token,
content: &str,
) -> Result<crate::models::api::MessageResponse, crate::error::BotError> {
if let (Some(channel_id), Some(msg_id)) = (&self.channel_id, &self.id) {
let params = MessageParams {
content: Some(content.to_string()),
msg_id: Some(msg_id.clone()),
event_id: self.event_id.clone(),
..Default::default()
};
api.post_message_with_params(token, channel_id, params)
.await
} else {
Err(crate::error::BotError::InvalidData(
"Missing channel_id or message_id for reply".to_string(),
))
}
}
pub fn has_content(&self) -> bool {
self.content.as_ref().is_some_and(|c| !c.is_empty())
}
pub fn has_attachments(&self) -> bool {
!self.attachments.is_empty()
}
pub fn has_mentions(&self) -> bool {
!self.mentions.is_empty()
}
pub fn is_from_bot(&self) -> bool {
self.author.as_ref().is_some_and(|a| a.bot.unwrap_or(false))
}
}
impl Default for Message {
fn default() -> Self {
Self::new()
}
}
impl HasId for Message {
fn id(&self) -> Option<&Snowflake> {
self.id.as_ref()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DirectMessage {
pub id: Option<Snowflake>,
pub content: Option<String>,
pub channel_id: Option<Snowflake>,
pub guild_id: Option<Snowflake>,
pub direct_message: Option<bool>,
pub author: Option<DirectMessageUser>,
pub member: Option<DirectMessageMember>,
pub message_reference: Option<MessageReference>,
pub attachments: Vec<MessageAttachment>,
pub seq: Option<u64>,
pub seq_in_channel: Option<String>,
pub src_guild_id: Option<Snowflake>,
pub timestamp: Option<Timestamp>,
pub event_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct DirectMessageSession {
pub guild_id: Option<Snowflake>,
pub channel_id: Option<Snowflake>,
pub create_time: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct DirectMessageToCreate {
pub source_guild_id: String,
pub recipient_id: String,
}
impl DirectMessageToCreate {
pub fn new(source_guild_id: impl Into<String>, recipient_id: impl Into<String>) -> Self {
Self {
source_guild_id: source_guild_id.into(),
recipient_id: recipient_id.into(),
}
}
}
impl DirectMessage {
pub fn new() -> Self {
Self {
id: None,
content: None,
channel_id: None,
guild_id: None,
direct_message: None,
author: None,
member: None,
message_reference: None,
attachments: Vec::new(),
seq: None,
seq_in_channel: None,
src_guild_id: None,
timestamp: None,
event_id: None,
}
}
pub fn from_data(_api: crate::api::BotApi, event_id: String, data: serde_json::Value) -> Self {
Self {
id: data.get("id").and_then(|v| v.as_str()).map(String::from),
content: data
.get("content")
.and_then(|v| v.as_str())
.map(String::from),
channel_id: data
.get("channel_id")
.and_then(|v| v.as_str())
.map(String::from),
guild_id: data
.get("guild_id")
.and_then(|v| v.as_str())
.map(String::from),
direct_message: data.get("direct_message").and_then(|v| v.as_bool()),
author: data
.get("author")
.map(|v| DirectMessageUser::from_data(v.clone())),
member: data
.get("member")
.map(|v| DirectMessageMember::from_data(v.clone())),
message_reference: data
.get("message_reference")
.map(|v| MessageReference::from_data(v.clone())),
attachments: data
.get("attachments")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|v| MessageAttachment::from_data(v.clone()))
.collect()
})
.unwrap_or_default(),
seq: data.get("seq").and_then(|v| v.as_u64()),
seq_in_channel: data
.get("seq_in_channel")
.and_then(|v| v.as_str())
.map(String::from),
src_guild_id: data
.get("src_guild_id")
.and_then(|v| v.as_str())
.map(String::from),
timestamp: data
.get("timestamp")
.and_then(|v| v.as_str())
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
.map(|dt| dt.with_timezone(&chrono::Utc)),
event_id: Some(event_id),
}
}
pub async fn reply(
&self,
api: &crate::api::BotApi,
token: &crate::token::Token,
content: &str,
) -> Result<crate::models::api::MessageResponse, crate::error::BotError> {
if let Some(guild_id) = &self.guild_id {
let params = DirectMessageParams {
content: Some(content.to_string()),
msg_id: self.id.clone(),
event_id: self.event_id.clone(),
..Default::default()
};
api.post_dms_with_params(token, guild_id, params).await
} else {
Err(crate::error::BotError::InvalidData(
"Missing guild_id for DM reply".to_string(),
))
}
}
}
impl Default for DirectMessage {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GroupMessage {
pub id: Option<Snowflake>,
pub content: Option<String>,
pub message_reference: Option<MessageReference>,
#[serde(default)]
pub mentions: Vec<GroupMessageUser>,
#[serde(default)]
pub attachments: Vec<MessageAttachment>,
pub msg_seq: Option<u64>,
pub timestamp: Option<Timestamp>,
pub author: Option<GroupMessageUser>,
pub group_openid: Option<String>,
#[serde(skip)]
pub event_id: Option<String>,
}
impl GroupMessage {
pub fn new() -> Self {
Self {
id: None,
content: None,
message_reference: None,
mentions: Vec::new(),
attachments: Vec::new(),
msg_seq: None,
timestamp: None,
author: None,
group_openid: None,
event_id: None,
}
}
pub fn from_data(_api: crate::api::BotApi, event_id: String, data: serde_json::Value) -> Self {
Self {
id: data.get("id").and_then(|v| v.as_str()).map(String::from),
content: data
.get("content")
.and_then(|v| v.as_str())
.map(String::from),
message_reference: data
.get("message_reference")
.map(|v| MessageReference::from_data(v.clone())),
mentions: data
.get("mentions")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|v| GroupMessageUser::from_data(v.clone()))
.collect()
})
.unwrap_or_default(),
attachments: data
.get("attachments")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|v| MessageAttachment::from_data(v.clone()))
.collect()
})
.unwrap_or_default(),
msg_seq: data.get("msg_seq").and_then(|v| v.as_u64()),
timestamp: data
.get("timestamp")
.and_then(|v| v.as_str())
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
.map(|dt| dt.with_timezone(&chrono::Utc)),
author: data
.get("author")
.map(|v| GroupMessageUser::from_data(v.clone())),
group_openid: data
.get("group_openid")
.and_then(|v| v.as_str())
.map(String::from),
event_id: Some(event_id),
}
}
pub async fn reply(
&self,
api: &crate::api::BotApi,
token: &crate::token::Token,
content: &str,
) -> Result<crate::models::api::MessageResponse, crate::error::BotError> {
if let (Some(group_openid), Some(msg_id)) = (&self.group_openid, &self.id) {
let params = GroupMessageParams {
msg_type: 0,
content: Some(content.to_string()),
msg_id: Some(msg_id.clone()),
event_id: self.event_id.clone(),
..Default::default()
};
api.post_group_message_with_params(token, group_openid, params)
.await
} else {
Err(crate::error::BotError::InvalidData(
"Missing group_openid or message_id for reply".to_string(),
))
}
}
}
impl Default for GroupMessage {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct C2CMessage {
pub id: Option<String>,
pub content: Option<String>,
pub message_reference: Option<MessageReference>,
pub mentions: Vec<C2CMessageUser>,
pub attachments: Vec<MessageAttachment>,
pub msg_seq: Option<u64>,
pub timestamp: Option<Timestamp>,
pub author: Option<C2CMessageUser>,
pub message_scene: Option<Value>,
pub event_id: Option<String>,
}
impl C2CMessage {
pub fn new() -> Self {
Self {
id: None,
content: None,
message_reference: None,
mentions: Vec::new(),
attachments: Vec::new(),
msg_seq: None,
timestamp: None,
author: None,
message_scene: None,
event_id: None,
}
}
pub fn from_data(_api: crate::api::BotApi, event_id: String, data: serde_json::Value) -> Self {
Self {
id: data.get("id").and_then(|v| v.as_str()).map(String::from),
content: data
.get("content")
.and_then(|v| v.as_str())
.map(String::from),
message_reference: data
.get("message_reference")
.map(|v| MessageReference::from_data(v.clone())),
mentions: data
.get("mentions")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|v| C2CMessageUser::from_data(v.clone()))
.collect()
})
.unwrap_or_default(),
attachments: data
.get("attachments")
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|v| MessageAttachment::from_data(v.clone()))
.collect()
})
.unwrap_or_default(),
msg_seq: data.get("msg_seq").and_then(|v| v.as_u64()),
timestamp: data
.get("timestamp")
.and_then(|v| v.as_str())
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
.map(|dt| dt.with_timezone(&chrono::Utc)),
author: data
.get("author")
.map(|v| C2CMessageUser::from_data(v.clone())),
message_scene: data.get("message_scene").cloned(),
event_id: Some(event_id),
}
}
pub async fn reply(
&self,
api: &crate::api::BotApi,
token: &crate::token::Token,
content: &str,
) -> Result<crate::models::api::MessageResponse, crate::error::BotError> {
if let (Some(user_openid), Some(msg_id)) = (
self.author.as_ref().and_then(|a| a.user_openid.as_ref()),
&self.id,
) {
let params = C2CMessageParams {
msg_type: 0,
content: Some(content.to_string()),
msg_id: Some(msg_id.clone()),
msg_seq: Some(1),
event_id: self.event_id.clone(),
..Default::default()
};
api.post_c2c_message_with_params(token, user_openid, params)
.await
} else {
Err(crate::error::BotError::InvalidData(
"Missing user_openid or message_id for C2C reply".to_string(),
))
}
}
}
impl Default for C2CMessage {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MessageAudit {
pub audit_id: Option<Snowflake>,
pub message_id: Option<Snowflake>,
pub channel_id: Option<Snowflake>,
pub guild_id: Option<Snowflake>,
pub audit_time: Option<Timestamp>,
pub create_time: Option<Timestamp>,
pub event_id: Option<String>,
}
impl MessageAudit {
pub fn new() -> Self {
Self {
audit_id: None,
message_id: None,
channel_id: None,
guild_id: None,
audit_time: None,
create_time: None,
event_id: None,
}
}
pub fn from_data(_api: crate::api::BotApi, event_id: String, data: serde_json::Value) -> Self {
Self {
audit_id: data
.get("audit_id")
.and_then(|v| v.as_str())
.map(String::from),
message_id: data
.get("message_id")
.and_then(|v| v.as_str())
.map(String::from),
audit_time: data
.get("audit_time")
.and_then(|v| v.as_str())
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
.map(|dt| dt.with_timezone(&chrono::Utc)),
channel_id: data
.get("channel_id")
.and_then(|v| v.as_str())
.map(String::from),
guild_id: data
.get("guild_id")
.and_then(|v| v.as_str())
.map(String::from),
create_time: data
.get("create_time")
.and_then(|v| v.as_str())
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
.map(|dt| dt.with_timezone(&chrono::Utc)),
event_id: Some(event_id),
}
}
}
impl Default for MessageAudit {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MessageUser {
pub id: Option<Snowflake>,
pub username: Option<String>,
pub bot: Option<bool>,
pub avatar: Option<String>,
}
impl MessageUser {
pub fn from_data(data: serde_json::Value) -> Self {
Self {
id: data.get("id").and_then(|v| v.as_str()).map(String::from),
username: data
.get("username")
.and_then(|v| v.as_str())
.map(String::from),
bot: data.get("bot").and_then(|v| v.as_bool()),
avatar: data
.get("avatar")
.and_then(|v| v.as_str())
.map(String::from),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DirectMessageUser {
pub id: Option<Snowflake>,
pub username: Option<String>,
pub avatar: Option<String>,
}
impl DirectMessageUser {
pub fn from_data(data: serde_json::Value) -> Self {
Self {
id: data.get("id").and_then(|v| v.as_str()).map(String::from),
username: data
.get("username")
.and_then(|v| v.as_str())
.map(String::from),
avatar: data
.get("avatar")
.and_then(|v| v.as_str())
.map(String::from),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GroupMessageUser {
pub id: Option<String>,
pub member_openid: Option<String>,
pub union_openid: Option<String>,
}
impl GroupMessageUser {
pub fn from_data(data: serde_json::Value) -> Self {
Self {
id: data.get("id").and_then(|v| v.as_str()).map(String::from),
member_openid: data
.get("member_openid")
.and_then(|v| v.as_str())
.map(String::from),
union_openid: data
.get("union_openid")
.and_then(|v| v.as_str())
.map(String::from),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct C2CMessageUser {
pub id: Option<String>,
pub union_openid: Option<String>,
pub user_openid: Option<String>,
}
impl C2CMessageUser {
pub fn from_data(data: serde_json::Value) -> Self {
Self {
id: data.get("id").and_then(|v| v.as_str()).map(String::from),
union_openid: data
.get("union_openid")
.and_then(|v| v.as_str())
.map(String::from),
user_openid: data
.get("user_openid")
.and_then(|v| v.as_str())
.map(String::from),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MessageMember {
pub nick: Option<String>,
pub roles: Option<Vec<Snowflake>>,
pub joined_at: Option<Timestamp>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DirectMessageMember {
pub joined_at: Option<Timestamp>,
}
impl DirectMessageMember {
pub fn from_data(data: serde_json::Value) -> Self {
Self {
joined_at: data
.get("joined_at")
.and_then(|v| v.as_str())
.and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
.map(|dt| dt.with_timezone(&chrono::Utc)),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MessageReference {
pub message_id: Option<Snowflake>,
}
impl MessageReference {
pub fn from_data(data: serde_json::Value) -> Self {
Self {
message_id: data
.get("message_id")
.and_then(|v| v.as_str())
.map(String::from),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MessageAttachment {
pub id: Option<Snowflake>,
pub filename: Option<String>,
pub content_type: Option<String>,
pub size: Option<u64>,
pub url: Option<String>,
pub width: Option<u32>,
pub height: Option<u32>,
}
impl MessageAttachment {
pub fn from_data(data: serde_json::Value) -> Self {
Self {
id: data.get("id").and_then(|v| v.as_str()).map(String::from),
filename: data
.get("filename")
.and_then(|v| v.as_str())
.map(String::from),
content_type: data
.get("content_type")
.and_then(|v| v.as_str())
.map(String::from),
size: data.get("size").and_then(|v| v.as_u64()),
url: data.get("url").and_then(|v| v.as_str()).map(String::from),
width: data.get("width").and_then(|v| v.as_u64()).map(|w| w as u32),
height: data
.get("height")
.and_then(|v| v.as_u64())
.map(|h| h as u32),
}
}
pub fn is_image(&self) -> bool {
self.content_type
.as_ref()
.is_some_and(|ct| ct.starts_with("image/"))
}
pub fn is_video(&self) -> bool {
self.content_type
.as_ref()
.is_some_and(|ct| ct.starts_with("video/"))
}
pub fn is_audio(&self) -> bool {
self.content_type
.as_ref()
.is_some_and(|ct| ct.starts_with("audio/"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_message_creation() {
let message = Message::new();
assert!(message.id.is_none());
assert!(message.content.is_none());
assert!(!message.has_content());
assert!(!message.has_attachments());
assert!(!message.has_mentions());
}
#[test]
fn test_message_with_content() {
let mut message = Message::new();
message.content = Some("Hello, world!".to_string());
assert!(message.has_content());
}
#[test]
fn test_message_attachment_types() {
let mut attachment = MessageAttachment {
id: Some("123".to_string()),
filename: Some("image.png".to_string()),
content_type: Some("image/png".to_string()),
size: Some(1024),
url: Some("https://example.com/image.png".to_string()),
width: Some(800),
height: Some(600),
};
assert!(attachment.is_image());
assert!(!attachment.is_video());
assert!(!attachment.is_audio());
attachment.content_type = Some("video/mp4".to_string());
assert!(!attachment.is_image());
assert!(attachment.is_video());
assert!(!attachment.is_audio());
}
#[test]
fn test_bot_detection() {
let mut message = Message::new();
message.author = Some(MessageUser {
id: Some("123".to_string()),
username: Some("Bot".to_string()),
bot: Some(true),
avatar: None,
});
assert!(message.is_from_bot());
message.author.as_mut().unwrap().bot = Some(false);
assert!(!message.is_from_bot());
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Ark {
pub template_id: Option<u32>,
pub kv: Option<Vec<ArkKv>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct MessageArk {
#[serde(skip_serializing_if = "Option::is_none")]
pub ark: Option<Ark>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ArkKv {
pub key: Option<String>,
pub value: Option<String>,
pub obj: Option<Vec<ArkObj>>,
}
pub type ArkKV = ArkKv;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ArkObj {
pub obj_kv: Option<Vec<ArkObjKv>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ArkObjKv {
pub key: Option<String>,
pub value: Option<String>,
}
pub type ArkObjKV = ArkObjKv;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct Embed {
pub title: Option<String>,
pub description: Option<String>,
pub url: Option<String>,
pub timestamp: Option<String>,
pub color: Option<u32>,
pub footer: Option<EmbedFooter>,
pub image: Option<EmbedImage>,
pub thumbnail: Option<EmbedThumbnail>,
pub video: Option<EmbedVideo>,
pub provider: Option<EmbedProvider>,
pub author: Option<EmbedAuthor>,
pub fields: Option<Vec<EmbedField>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EmbedFooter {
pub text: Option<String>,
pub icon_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EmbedImage {
pub url: Option<String>,
pub width: Option<u32>,
pub height: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EmbedThumbnail {
pub url: Option<String>,
pub width: Option<u32>,
pub height: Option<u32>,
}
pub type MessageEmbedThumbnail = EmbedThumbnail;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EmbedVideo {
pub url: Option<String>,
pub width: Option<u32>,
pub height: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EmbedProvider {
pub name: Option<String>,
pub url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EmbedAuthor {
pub name: Option<String>,
pub url: Option<String>,
pub icon_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct EmbedField {
pub name: Option<String>,
pub value: Option<String>,
pub inline: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct Keyboard {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<KeyboardContent>,
}
pub type ActionType = u32;
pub type PermissionType = u32;
pub const ACTION_TYPE_URL: ActionType = 0;
pub const ACTION_TYPE_CALLBACK: ActionType = 1;
pub const ACTION_TYPE_AT_BOT: ActionType = 2;
pub const ACTION_TYPE_MQQ_API: ActionType = 3;
pub const ACTION_TYPE_SUBSCRIBE: ActionType = 4;
#[allow(non_upper_case_globals)]
pub const ActionTypeURL: ActionType = ACTION_TYPE_URL;
#[allow(non_upper_case_globals)]
pub const ActionTypeCallback: ActionType = ACTION_TYPE_CALLBACK;
#[allow(non_upper_case_globals)]
pub const ActionTypeAtBot: ActionType = ACTION_TYPE_AT_BOT;
#[allow(non_upper_case_globals)]
pub const ActionTypeMQQAPI: ActionType = ACTION_TYPE_MQQ_API;
#[allow(non_upper_case_globals)]
pub const ActionTypeSubscribe: ActionType = ACTION_TYPE_SUBSCRIBE;
pub const PERMISSION_TYPE_SPECIFY_USER_IDS: PermissionType = 0;
pub const PERMISSION_TYPE_MANAGER: PermissionType = 1;
pub const PERMISSION_TYPE_ALL: PermissionType = 2;
pub const PERMISSION_TYPE_SPECIFY_ROLE_IDS: PermissionType = 3;
#[allow(non_upper_case_globals)]
pub const PermissionTypeSpecifyUserIDs: PermissionType = PERMISSION_TYPE_SPECIFY_USER_IDS;
#[allow(non_upper_case_globals)]
pub const PermissionTypManager: PermissionType = PERMISSION_TYPE_MANAGER;
#[allow(non_upper_case_globals)]
pub const PermissionTypAll: PermissionType = PERMISSION_TYPE_ALL;
#[allow(non_upper_case_globals)]
pub const PermissionTypSpecifyRoleIDs: PermissionType = PERMISSION_TYPE_SPECIFY_ROLE_IDS;
pub type MessageKeyboard = Keyboard;
pub type CustomKeyboard = KeyboardContent;
pub type Row = KeyboardRow;
pub type Button = KeyboardButton;
pub type RenderData = KeyboardButtonRenderData;
pub type Action = KeyboardButtonAction;
pub type Permission = KeyboardButtonPermission;
pub type SubscribeData = KeyboardSubscribeData;
pub type TemplateID = KeyboardTemplateId;
pub type Modal = KeyboardModal;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct KeyboardContent {
#[serde(skip_serializing_if = "Option::is_none")]
pub rows: Option<Vec<KeyboardRow>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub style: Option<KeyboardStyle>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct KeyboardStyle {
#[serde(skip_serializing_if = "Option::is_none")]
pub font_size: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct KeyboardRow {
#[serde(skip_serializing_if = "Option::is_none")]
pub buttons: Option<Vec<KeyboardButton>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct KeyboardButton {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub render_data: Option<KeyboardButtonRenderData>,
#[serde(skip_serializing_if = "Option::is_none")]
pub action: Option<KeyboardButtonAction>,
#[serde(skip_serializing_if = "Option::is_none")]
pub group_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct KeyboardButtonRenderData {
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub visited_label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub style: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct KeyboardButtonAction {
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub action_type: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission: Option<KeyboardButtonPermission>,
#[serde(skip_serializing_if = "Option::is_none")]
pub click_limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reply: Option<bool>,
pub enter: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub at_bot_show_channel_list: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe_data: Option<KeyboardSubscribeData>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modal: Option<KeyboardModal>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct KeyboardButtonPermission {
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub permission_type: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub specify_role_ids: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub specify_user_ids: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct KeyboardSubscribeData {
#[serde(skip_serializing_if = "Option::is_none")]
pub template_ids: Option<Vec<KeyboardTemplateId>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct KeyboardTemplateId {
#[serde(skip_serializing_if = "Option::is_none")]
pub template_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_template_id: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct KeyboardModal {
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub confirm_text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cancel_text: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KeyboardPayload {
pub content: serde_json::Value,
}
pub type Markdown = MarkdownPayload;
pub type MarkdownParams = MarkdownParam;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct MarkdownPayload {
#[serde(skip_serializing_if = "Option::is_none")]
pub template_id: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_template_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<Vec<MarkdownParam>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub style: Option<MarkdownStyle>,
#[serde(skip_serializing_if = "Option::is_none")]
pub process_msg: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct MarkdownStyle {
#[serde(skip_serializing_if = "Option::is_none")]
pub main_font_size: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub layout: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MarkdownParam {
pub key: Option<String>,
pub values: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct Media {
pub file_info: Option<String>,
pub ttl: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Reference {
pub message_id: Option<String>,
pub ignore_get_message_error: Option<bool>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(from = "u8", into = "u8")]
#[repr(u8)]
pub enum SendType {
Text = 1,
RichMedia = 2,
Unknown(u8),
}
impl From<u8> for SendType {
fn from(value: u8) -> Self {
match value {
1 => Self::Text,
2 => Self::RichMedia,
other => Self::Unknown(other),
}
}
}
impl From<SendType> for u8 {
fn from(send_type: SendType) -> Self {
match send_type {
SendType::Text => 1,
SendType::RichMedia => 2,
SendType::Unknown(value) => value,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(from = "u32", into = "u32")]
#[repr(u32)]
pub enum MessageCreateType {
Text = 0,
Markdown = 2,
Ark = 3,
Embed = 4,
At = 5,
InputNotify = 6,
RichMedia = 7,
Unknown(u32),
}
#[allow(non_upper_case_globals)]
pub const TextMsg: MessageCreateType = MessageCreateType::Text;
#[allow(non_upper_case_globals)]
pub const MarkdownMsg: MessageCreateType = MessageCreateType::Markdown;
#[allow(non_upper_case_globals)]
pub const ArkMsg: MessageCreateType = MessageCreateType::Ark;
#[allow(non_upper_case_globals)]
pub const EmbedMsg: MessageCreateType = MessageCreateType::Embed;
#[allow(non_upper_case_globals)]
pub const ATMsg: MessageCreateType = MessageCreateType::At;
#[allow(non_upper_case_globals)]
pub const InputNotifyMsg: MessageCreateType = MessageCreateType::InputNotify;
#[allow(non_upper_case_globals)]
pub const RichMediaMsg: MessageCreateType = MessageCreateType::RichMedia;
#[allow(non_upper_case_globals)]
pub const RichMedia: SendType = SendType::RichMedia;
impl From<u32> for MessageCreateType {
fn from(value: u32) -> Self {
match value {
0 => Self::Text,
2 => Self::Markdown,
3 => Self::Ark,
4 => Self::Embed,
5 => Self::At,
6 => Self::InputNotify,
7 => Self::RichMedia,
other => Self::Unknown(other),
}
}
}
impl From<MessageCreateType> for u32 {
fn from(message_type: MessageCreateType) -> Self {
match message_type {
MessageCreateType::Text => 0,
MessageCreateType::Markdown => 2,
MessageCreateType::Ark => 3,
MessageCreateType::Embed => 4,
MessageCreateType::At => 5,
MessageCreateType::InputNotify => 6,
MessageCreateType::RichMedia => 7,
MessageCreateType::Unknown(value) => value,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct InputNotify {
#[serde(skip_serializing_if = "Option::is_none")]
pub input_type: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_second: Option<i32>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct MediaInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub file_info: Option<String>,
}
impl From<Media> for MediaInfo {
fn from(media: Media) -> Self {
Self {
file_info: media.file_info,
}
}
}
impl From<MediaInfo> for Media {
fn from(media: MediaInfo) -> Self {
Self {
file_info: media.file_info,
ttl: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct Stream {
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub index: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reset: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct PromptKeyboard {
#[serde(skip_serializing_if = "Option::is_none")]
pub keyboard: Option<Keyboard>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct ActionButton {
#[serde(skip_serializing_if = "Option::is_none")]
pub template_id: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub callback_data: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub feedback: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tts: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub re_generate: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_generate: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct MessageToCreate {
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_type: Option<MessageCreateType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub embed: Option<Embed>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ark: Option<Ark>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message_reference: Option<Reference>,
#[serde(skip_serializing_if = "Option::is_none")]
pub markdown: Option<MarkdownPayload>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keyboard: Option<Keyboard>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timestamp: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_seq: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_notify: Option<InputNotify>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media: Option<MediaInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_keyboard: Option<PromptKeyboard>,
#[serde(skip_serializing_if = "Option::is_none")]
pub action_button: Option<ActionButton>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<Stream>,
#[serde(skip_serializing_if = "Option::is_none")]
pub feature_id: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_image: Option<String>,
}
impl MessageToCreate {
pub fn new_text(content: impl Into<String>) -> Self {
Self {
content: Some(content.into()),
..Default::default()
}
}
pub fn with_file_image(mut self, data: &[u8]) -> Self {
self.file_image = Some(base64::engine::general_purpose::STANDARD.encode(data));
self
}
pub fn with_reply(mut self, message_id: impl Into<String>) -> Self {
self.msg_id = Some(message_id.into());
self
}
pub const fn send_type(&self) -> SendType {
SendType::Text
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct RichMediaMessage {
#[serde(skip_serializing_if = "Option::is_none")]
pub event_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_type: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub srv_send_msg: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_seq: Option<i64>,
}
impl RichMediaMessage {
pub fn new(file_type: u64, url: impl Into<String>) -> Self {
Self {
file_type: Some(file_type),
url: Some(url.into()),
..Default::default()
}
}
pub const fn send_type(&self) -> SendType {
SendType::RichMedia
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ApiMessage {
Message(Box<MessageToCreate>),
RichMedia(RichMediaMessage),
}
impl ApiMessage {
pub const fn send_type(&self) -> SendType {
match self {
Self::Message(message) => message.send_type(),
Self::RichMedia(message) => message.send_type(),
}
}
}
impl Serialize for ApiMessage {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Self::Message(message) => message.serialize(serializer),
Self::RichMedia(message) => message.serialize(serializer),
}
}
}
impl From<MessageToCreate> for ApiMessage {
fn from(message: MessageToCreate) -> Self {
Self::Message(Box::new(message))
}
}
impl From<RichMediaMessage> for ApiMessage {
fn from(message: RichMediaMessage) -> Self {
Self::RichMedia(message)
}
}
impl From<KeyboardPayload> for Keyboard {
fn from(payload: KeyboardPayload) -> Self {
serde_json::from_value(payload.content).unwrap_or_default()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MessagePagerType {
Around,
Before,
After,
}
#[allow(non_upper_case_globals)]
pub const MPTAround: MessagePagerType = MessagePagerType::Around;
#[allow(non_upper_case_globals)]
pub const MPTBefore: MessagePagerType = MessagePagerType::Before;
#[allow(non_upper_case_globals)]
pub const MPTAfter: MessagePagerType = MessagePagerType::After;
impl MessagePagerType {
pub const fn as_str(self) -> &'static str {
match self {
Self::Around => "around",
Self::Before => "before",
Self::After => "after",
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct MessagesPager {
#[serde(skip)]
pub pager_type: Option<MessagePagerType>,
#[serde(skip)]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<String>,
}
impl MessagesPager {
pub fn new(
pager_type: Option<MessagePagerType>,
id: Option<impl Into<String>>,
limit: Option<impl ToString>,
) -> Self {
Self {
pager_type,
id: id.map(Into::into),
limit: limit.map(|value| value.to_string()),
}
}
pub fn query_params(&self) -> std::collections::HashMap<String, String> {
let mut query = std::collections::HashMap::new();
if let Some(limit) = &self.limit {
query.insert("limit".to_string(), limit.clone());
}
if let (Some(pager_type), Some(id)) = (self.pager_type, &self.id) {
query.insert(pager_type.as_str().to_string(), id.clone());
}
query
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct SettingGuide {
pub guild_id: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct SettingGuideToCreate {
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub setting_guide: Option<SettingGuide>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct MessageParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_type: Option<MessageCreateType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub embed: Option<Embed>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ark: Option<Ark>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message_reference: Option<Reference>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_image: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub markdown: Option<MarkdownPayload>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keyboard: Option<Keyboard>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timestamp: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_seq: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_notify: Option<InputNotify>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media: Option<MediaInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_keyboard: Option<PromptKeyboard>,
#[serde(skip_serializing_if = "Option::is_none")]
pub action_button: Option<ActionButton>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<Stream>,
#[serde(skip_serializing_if = "Option::is_none")]
pub feature_id: Option<u32>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GroupMessageParams {
#[serde(skip_serializing_if = "is_zero_u32")]
pub msg_type: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub embed: Option<Embed>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ark: Option<Ark>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message_reference: Option<Reference>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media: Option<Media>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_seq: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub markdown: Option<MarkdownPayload>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keyboard: Option<KeyboardPayload>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timestamp: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_notify: Option<InputNotify>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_keyboard: Option<PromptKeyboard>,
#[serde(skip_serializing_if = "Option::is_none")]
pub action_button: Option<ActionButton>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<Stream>,
#[serde(skip_serializing_if = "Option::is_none")]
pub feature_id: Option<u32>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct C2CMessageParams {
#[serde(skip_serializing_if = "is_zero_u32")]
pub msg_type: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub embed: Option<Embed>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ark: Option<Ark>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message_reference: Option<Reference>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media: Option<Media>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_seq: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub markdown: Option<MarkdownPayload>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keyboard: Option<KeyboardPayload>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timestamp: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_notify: Option<InputNotify>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_keyboard: Option<PromptKeyboard>,
#[serde(skip_serializing_if = "Option::is_none")]
pub action_button: Option<ActionButton>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<Stream>,
#[serde(skip_serializing_if = "Option::is_none")]
pub feature_id: Option<u32>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct DirectMessageParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_type: Option<MessageCreateType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub embed: Option<Embed>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ark: Option<Ark>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message_reference: Option<Reference>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_image: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub markdown: Option<MarkdownPayload>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keyboard: Option<Keyboard>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timestamp: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_seq: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub input_notify: Option<InputNotify>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media: Option<MediaInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prompt_keyboard: Option<PromptKeyboard>,
#[serde(skip_serializing_if = "Option::is_none")]
pub action_button: Option<ActionButton>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<Stream>,
#[serde(skip_serializing_if = "Option::is_none")]
pub feature_id: Option<u32>,
}
impl MessageParams {
pub fn new_text(content: impl Into<String>) -> Self {
Self {
content: Some(content.into()),
..Default::default()
}
}
pub fn with_file_image(mut self, data: &[u8]) -> Self {
self.file_image = Some(base64::engine::general_purpose::STANDARD.encode(data));
self
}
pub fn with_reply(mut self, message_id: impl Into<String>) -> Self {
self.msg_id = Some(message_id.into());
self
}
pub fn into_message_to_create(self) -> MessageToCreate {
self.into()
}
}
impl GroupMessageParams {
pub fn new_text(content: impl Into<String>) -> Self {
Self {
msg_type: 0,
content: Some(content.into()),
..Default::default()
}
}
pub fn with_reply(mut self, message_id: impl Into<String>) -> Self {
self.msg_id = Some(message_id.into());
self
}
pub fn into_message_to_create(self) -> MessageToCreate {
self.into()
}
}
impl C2CMessageParams {
pub fn new_text(content: impl Into<String>) -> Self {
Self {
msg_type: 0,
content: Some(content.into()),
..Default::default()
}
}
pub fn with_reply(mut self, message_id: impl Into<String>) -> Self {
self.msg_id = Some(message_id.into());
self
}
pub fn into_message_to_create(self) -> MessageToCreate {
self.into()
}
}
impl DirectMessageParams {
pub fn new_text(content: impl Into<String>) -> Self {
Self {
content: Some(content.into()),
..Default::default()
}
}
pub fn with_file_image(mut self, data: &[u8]) -> Self {
self.file_image = Some(base64::engine::general_purpose::STANDARD.encode(data));
self
}
pub fn with_reply(mut self, message_id: impl Into<String>) -> Self {
self.msg_id = Some(message_id.into());
self
}
pub fn into_message_to_create(self) -> MessageToCreate {
self.into()
}
}
impl From<MessageParams> for MessageToCreate {
fn from(params: MessageParams) -> Self {
Self {
content: params.content,
msg_type: params.msg_type,
embed: params.embed,
ark: params.ark,
image: params.image,
msg_id: params.msg_id,
message_reference: params.message_reference,
markdown: params.markdown,
keyboard: params.keyboard,
event_id: params.event_id,
timestamp: params.timestamp,
msg_seq: params.msg_seq,
subscribe_id: params.subscribe_id,
input_notify: params.input_notify,
media: params.media,
prompt_keyboard: params.prompt_keyboard,
action_button: params.action_button,
stream: params.stream,
feature_id: params.feature_id,
file_image: params.file_image,
}
}
}
impl From<DirectMessageParams> for MessageToCreate {
fn from(params: DirectMessageParams) -> Self {
Self {
content: params.content,
msg_type: params.msg_type,
embed: params.embed,
ark: params.ark,
image: params.image,
msg_id: params.msg_id,
message_reference: params.message_reference,
markdown: params.markdown,
keyboard: params.keyboard,
event_id: params.event_id,
timestamp: params.timestamp,
msg_seq: params.msg_seq,
subscribe_id: params.subscribe_id,
input_notify: params.input_notify,
media: params.media,
prompt_keyboard: params.prompt_keyboard,
action_button: params.action_button,
stream: params.stream,
feature_id: params.feature_id,
file_image: params.file_image,
}
}
}
impl From<GroupMessageParams> for MessageToCreate {
fn from(params: GroupMessageParams) -> Self {
Self {
content: params.content,
msg_type: Some(MessageCreateType::from(params.msg_type)),
embed: params.embed,
ark: params.ark,
msg_id: params.msg_id,
message_reference: params.message_reference,
markdown: params.markdown,
keyboard: params.keyboard.map(Into::into),
event_id: params.event_id,
timestamp: params.timestamp,
msg_seq: params.msg_seq,
subscribe_id: params.subscribe_id,
input_notify: params.input_notify,
media: params.media.map(Into::into),
prompt_keyboard: params.prompt_keyboard,
action_button: params.action_button,
stream: params.stream,
feature_id: params.feature_id,
..Default::default()
}
}
}
impl From<C2CMessageParams> for MessageToCreate {
fn from(params: C2CMessageParams) -> Self {
Self {
content: params.content,
msg_type: Some(MessageCreateType::from(params.msg_type)),
embed: params.embed,
ark: params.ark,
msg_id: params.msg_id,
message_reference: params.message_reference,
markdown: params.markdown,
keyboard: params.keyboard.map(Into::into),
event_id: params.event_id,
timestamp: params.timestamp,
msg_seq: params.msg_seq,
subscribe_id: params.subscribe_id,
input_notify: params.input_notify,
media: params.media.map(Into::into),
prompt_keyboard: params.prompt_keyboard,
action_button: params.action_button,
stream: params.stream,
feature_id: params.feature_id,
..Default::default()
}
}
}