use crate::models::{HasId, Snowflake, Timestamp};
use base64::Engine;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[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 author: Option<MessageUser>,
pub member: Option<MessageMember>,
pub message_reference: Option<MessageReference>,
pub mentions: Vec<MessageUser>,
pub attachments: Vec<MessageAttachment>,
pub seq: Option<u64>,
pub seq_in_channel: Option<String>,
pub timestamp: Option<Timestamp>,
pub event_id: Option<String>,
}
impl Message {
pub fn new() -> Self {
Self {
id: None,
content: None,
channel_id: None,
guild_id: None,
author: None,
member: None,
message_reference: None,
mentions: Vec::new(),
attachments: Vec::new(),
seq: None,
seq_in_channel: 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),
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(),
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)),
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>,
}
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)]
pub struct ArkKv {
pub key: Option<String>,
pub value: Option<String>,
pub obj: Option<Vec<ArkObj>>,
}
#[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>,
}
#[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>,
}
#[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)]
pub struct Keyboard {
pub content: Option<KeyboardContent>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KeyboardContent {
pub rows: Option<Vec<KeyboardRow>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KeyboardRow {
pub buttons: Option<Vec<KeyboardButton>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KeyboardButton {
pub id: Option<String>,
pub render_data: Option<KeyboardButtonRenderData>,
pub action: Option<KeyboardButtonAction>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KeyboardButtonRenderData {
pub label: Option<String>,
pub visited_label: Option<String>,
pub style: Option<u32>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KeyboardButtonAction {
#[serde(rename = "type")]
pub action_type: Option<u32>,
pub permission: Option<KeyboardButtonPermission>,
pub click_limit: Option<u32>,
pub data: Option<String>,
pub reply: Option<bool>,
pub enter: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KeyboardButtonPermission {
#[serde(rename = "type")]
pub permission_type: Option<u32>,
pub specify_role_ids: Option<Vec<String>>,
pub specify_user_ids: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KeyboardPayload {
pub content: serde_json::Value,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct MarkdownPayload {
pub template_id: Option<String>,
pub custom_template_id: Option<String>,
pub params: Option<Vec<MarkdownParam>>,
pub content: 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)]
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, Default, Serialize)]
pub struct MessageParams {
#[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 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>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct GroupMessageParams {
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>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct C2CMessageParams {
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>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct DirectMessageParams {
#[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 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>,
}
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
}
}
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
}
}
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
}
}
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
}
}