use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(tag = "type")]
pub enum MessageSegment {
#[serde(rename = "text")]
Text { data: TextData },
#[serde(rename = "face")]
Face { data: FaceData },
#[serde(rename = "mface")]
Mface { data: MfaceData },
#[serde(rename = "at")]
At { data: AtData },
#[serde(rename = "image")]
Image { data: ImageData },
#[serde(rename = "record")]
Record { data: RecordData },
#[serde(rename = "video")]
Video { data: VideoData },
#[serde(rename = "file")]
File { data: File },
#[serde(rename = "rps")]
Rps {
#[serde(default = "default_value")]
data: Value,
},
#[serde(rename = "dice")]
Dice {
#[serde(default = "default_value")]
data: Value,
},
#[serde(rename = "shake")]
Shake {
#[serde(default = "default_value")]
data: Value,
},
#[serde(rename = "poke")]
Poke { data: PokeData },
#[serde(rename = "anonymous")]
Anonymous { data: AnonymousData },
#[serde(rename = "share")]
Share { data: ShareData },
#[serde(rename = "contact")]
Contact { data: ContactData },
#[serde(rename = "location")]
Location { data: LocationData },
#[serde(rename = "music")]
Music { data: MusicData },
#[serde(rename = "custom_music")]
CustomMusic { data: CustomMusicData },
#[serde(rename = "reply")]
Reply { data: ReplyData },
#[serde(rename = "forward")]
Forward { data: ForwardData },
#[serde(rename = "node")]
Node { data: NodeData },
#[serde(rename = "node")]
CustomNode { data: CustomNodeData },
#[serde(rename = "xml")]
Xml { data: XmlData },
#[serde(rename = "json")]
Json { data: JsonData },
}
fn default_value() -> Value {
json!({})
}
impl MessageSegment {
pub fn text(text: impl Into<String>) -> Self {
MessageSegment::Text {
data: TextData { text: text.into() },
}
}
pub fn face(id: impl Into<String>) -> Self {
MessageSegment::Face {
data: FaceData { id: id.into() },
}
}
pub fn mface(
summary: impl Into<String>,
url: impl Into<String>,
emoji_id: impl Into<String>,
emoji_package_id: impl Into<String>,
key: impl Into<String>,
) -> Self {
MessageSegment::Mface {
data: MfaceData {
summary: summary.into(),
url: url.into(),
emoji_id: emoji_id.into(),
emoji_package_id: emoji_package_id.into(),
key: key.into(),
},
}
}
pub fn at(qq: impl Into<String>) -> Self {
MessageSegment::At {
data: AtData { qq: qq.into() },
}
}
pub fn easy_image(file: impl Into<String>, summary: Option<impl Into<String>>) -> Self {
MessageSegment::Image {
data: ImageData {
file: file.into(),
r#type: None,
url: None,
cache: None,
proxy: None,
timeout: None,
summary: summary.map(|s| s.into()),
},
}
}
pub fn image(
file: impl Into<String>,
summary: Option<impl Into<String>>,
r#type: Option<impl Into<String>>,
cache: Option<bool>,
proxy: Option<bool>,
timeout: Option<u32>,
) -> Self {
MessageSegment::Image {
data: ImageData {
file: file.into(),
r#type: r#type.map(|t| t.into()),
url: None,
cache: cache.map(|c| if c { 1 } else { 0 }),
proxy: proxy.map(|p| if p { 1 } else { 0 }),
timeout,
summary: summary.map(|s| s.into()),
},
}
}
pub fn record(
file: impl Into<String>,
magic: Option<bool>,
url: Option<impl Into<String>>,
cache: Option<bool>,
proxy: Option<bool>,
timeout: Option<u32>,
) -> Self {
MessageSegment::Record {
data: RecordData {
file: file.into(),
magic: magic.map(|m| if m { 1 } else { 0 }),
url: url.map(|u| u.into()),
cache: cache.map(|c| if c { 1 } else { 0 }),
proxy: proxy.map(|p| if p { 1 } else { 0 }),
timeout,
},
}
}
pub fn video(
file: impl Into<String>,
url: Option<impl Into<String>>,
cache: Option<bool>,
proxy: Option<bool>,
timeout: Option<u32>,
) -> Self {
MessageSegment::Video {
data: VideoData {
file: file.into(),
url: url.map(|u| u.into()),
cache: cache.map(|c| if c { 1 } else { 0 }),
proxy: proxy.map(|p| if p { 1 } else { 0 }),
timeout,
},
}
}
pub fn file(file: impl Into<String>, name: Option<impl Into<String>>) -> Self {
MessageSegment::File {
data: File {
file: file.into(),
name: name.map(|n| n.into()),
path: String::with_capacity(0),
url: None,
file_id: String::with_capacity(0),
file_size: String::with_capacity(0),
},
}
}
pub fn poke(r#type: String, id: impl Into<String>) -> Self {
MessageSegment::Poke {
data: PokeData {
r#type,
id: id.into(),
},
}
}
pub fn anonymous(ignore: Option<bool>) -> Self {
MessageSegment::Anonymous {
data: AnonymousData {
ignore: ignore.map(|i| if i { 1 } else { 0 }),
},
}
}
pub fn contact(r#type: ContactType, id: impl Into<String>) -> Self {
MessageSegment::Contact {
data: ContactData {
r#type,
id: id.into(),
},
}
}
pub fn music(r#type: String, id: impl Into<String>) -> Self {
MessageSegment::Music {
data: MusicData {
r#type,
id: id.into(),
},
}
}
pub fn music_custom(
r#type: impl Into<String>,
url: impl Into<String>,
audio: impl Into<String>,
title: impl Into<String>,
content: Option<impl Into<String>>,
image: Option<impl Into<String>>,
) -> Self {
MessageSegment::CustomMusic {
data: CustomMusicData {
r#type: r#type.into(),
url: url.into(),
audio: audio.into(),
title: title.into(),
content: content.map(|c| c.into()),
image: image.map(|i| i.into()),
},
}
}
pub fn reply(id: impl Into<String>) -> Self {
MessageSegment::Reply {
data: ReplyData { id: id.into() },
}
}
pub fn forward(id: impl Into<String>) -> Self {
MessageSegment::Forward {
data: ForwardData { id: id.into() },
}
}
pub fn node(id: impl Into<String>) -> Self {
MessageSegment::Node {
data: NodeData { id: id.into() },
}
}
pub fn easy_custom_node(content: Vec<MessageSegment>) -> Self {
MessageSegment::CustomNode {
data: CustomNodeData {
name: None,
uin: None,
content,
},
}
}
pub fn custom_node(uin: i64, name: impl Into<String>, content: Vec<MessageSegment>) -> Self {
MessageSegment::CustomNode {
data: CustomNodeData {
name: Some(name.into()),
uin: Some(uin),
content,
},
}
}
pub fn rps() -> Self {
MessageSegment::Rps {
data: default_value(),
}
}
pub fn dice() -> Self {
MessageSegment::Dice {
data: default_value(),
}
}
pub fn shake() -> Self {
MessageSegment::Shake {
data: default_value(),
}
}
pub fn share(
url: impl Into<String>,
title: impl Into<String>,
content: Option<impl Into<String>>,
image: Option<impl Into<String>>,
) -> Self {
MessageSegment::Share {
data: ShareData {
url: url.into(),
title: title.into(),
content: content.map(|c| c.into()),
image: image.map(|i| i.into()),
},
}
}
pub fn location(
lat: impl Into<String>,
lon: impl Into<String>,
title: Option<impl Into<String>>,
content: Option<impl Into<String>>,
) -> Self {
MessageSegment::Location {
data: LocationData {
lat: lat.into(),
lon: lon.into(),
title: title.map(|t| t.into()),
content: content.map(|c| c.into()),
},
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct TextData {
pub text: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct FaceData {
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct ImageData {
pub file: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub summary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cache: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub proxy: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<u32>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct RecordData {
pub file: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub magic: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cache: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub proxy: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<u32>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct VideoData {
pub file: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cache: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub proxy: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<u32>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct AtData {
pub qq: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct PokeData {
pub r#type: String,
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct AnonymousData {
#[serde(skip_serializing_if = "Option::is_none")]
pub ignore: Option<u8>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct ShareData {
pub url: String,
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum ContactType {
#[serde(rename = "group")]
Group,
#[serde(rename = "qq")]
QQ,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct ContactData {
pub r#type: ContactType,
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct LocationData {
pub lat: String,
pub lon: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct MusicData {
pub r#type: String,
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct CustomMusicData {
pub r#type: String,
pub url: String,
pub audio: String,
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct ReplyData {
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct ForwardData {
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct NodeData {
pub id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct CustomNodeData {
pub name: Option<String>,
pub uin: Option<i64>,
pub content: Vec<MessageSegment>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct XmlData {
pub data: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct JsonData {
pub data: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct MfaceData {
pub summary: String,
pub url: String,
pub emoji_id: String,
pub emoji_package_id: String,
pub key: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct File {
pub file: String,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub name: Option<String>,
#[serde(skip_serializing)]
pub path: String,
#[serde(skip_serializing)]
pub url: Option<String>,
#[serde(skip_serializing)]
pub file_id: String,
#[serde(skip_serializing)]
pub file_size: String,
}