socket-bot/ica-rs/src/data_struct/tailchat/messages.rs
2024-06-28 23:19:10 +08:00

239 lines
6.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::fmt::Display;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value as JsonValue};
use crate::data_struct::tailchat::{ConverseId, GroupId, MessageId, UserId};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReceiveMessage {
/// 消息ID
#[serde(rename = "_id")]
pub msg_id: MessageId,
/// 消息内容
pub content: String,
/// 发送者ID
#[serde(rename = "author")]
pub sender_id: UserId,
/// 服务器ID
/// 在私聊中不存在
#[serde(rename = "groupId")]
pub group_id: Option<GroupId>,
/// 会话ID
#[serde(rename = "converseId")]
pub converse_id: ConverseId,
/// 是否有回复?
#[serde(rename = "hasRecall")]
pub has_recall: bool,
/// 暂时懒得解析这玩意
/// 准确来说是不确定内容, 毕竟没细看 API
pub meta: Option<JsonValue>,
/// 也懒得解析这玩意
pub reactions: Vec<JsonValue>,
/// 创建时间
#[serde(rename = "createdAt")]
pub created_at: String,
/// 更新时间
#[serde(rename = "updatedAt")]
pub updated_at: String,
}
impl ReceiveMessage {
pub fn is_reply(&self) -> bool {
if let Some(meta) = &self.meta {
meta.get("reply").is_some()
} else {
false
}
}
pub fn is_from_self(&self) -> bool {
crate::MainStatus::global_tailchat_status().user_id == self.sender_id
}
/// 创建一个对这条消息的回复
pub fn as_reply(&self) -> SendingMessage {
SendingMessage::new(
"".to_string(),
self.converse_id.clone(),
self.group_id.clone(),
Some(ReplyMeta::from_receive_message(self)),
)
}
/// 回复这条消息
pub fn reply_with(&self, content: &str) -> SendingMessage {
SendingMessage::new(
content.to_string(),
self.converse_id.clone(),
self.group_id.clone(),
Some(ReplyMeta::from_receive_message(self)),
)
}
}
impl Display for ReceiveMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// msgid|groupid-converseid|senderid|content
write!(
f,
"{}|{:?}-{}|{}|{}",
self.msg_id, self.group_id, self.converse_id, self.sender_id, self.content
)
}
}
#[derive(Debug, Clone, Default)]
pub enum SendingFile {
#[default]
None,
/// 需要生成
/// [img height=1329 width=1918]{BACKEND}/static/files/6602e20d7b8d10675758e36b/8db505b87bdf9fb309467abcec4d8e2a.png[/img]
Image { file: Vec<u8>, name: String },
/// [card type=file url={BACKEND}/static/files/6602e20d7b8d10675758e36b/9df28943d17b9713cb0ea9625f37d015.wav]Engine.wav[/card]
File { file: Vec<u8>, name: String },
}
impl SendingFile {
pub fn is_some(&self) -> bool { !matches!(self, Self::None) }
pub fn is_image(&self) -> bool { matches!(self, Self::Image { .. }) }
pub fn is_file(&self) -> bool { matches!(self, Self::File { .. }) }
pub fn file_data(&self) -> Vec<u8> {
match self {
Self::Image { file, .. } => file.clone(),
Self::File { file, .. } => file.clone(),
_ => vec![],
}
}
pub fn file_name(&self) -> String {
match self {
Self::Image { name, .. } => name.clone(),
Self::File { name, .. } => name.clone(),
_ => "".to_string(),
}
}
pub fn gen_markdown(&self, backend_path: &str) -> String {
match self {
Self::Image { .. } => {
format!("[img]{}[/img]", backend_path)
}
Self::File { name, .. } => {
format!("[card type=file url={}]{}[/card]", backend_path, name)
}
_ => unreachable!(),
}
}
}
#[derive(Debug, Clone, Serialize)]
/// 将要发送的消息
///
/// 发送时:
/// - `content`: 回复的消息内容
/// - `converseId`: 会话ID
/// - `groupId`: 服务器ID
/// - `meta`: 回复的消息的元数据 可能为空
/// - `mentions`: 被回复的人的ID (可以是多个)
/// - `reply`: 被回复的消息
/// - `_id`: 被回复的消息ID
/// - `author`: 被回复的消息的发送者ID
/// - `content`: 被回复的消息内容
pub struct SendingMessage {
/// 消息内容
///
/// 其实还有个 plain, 就是不知道干啥的
pub content: String,
/// 会话ID
#[serde(rename = "converseId")]
pub converse_id: ConverseId,
/// 服务器ID
#[serde(rename = "groupId")]
pub group_id: Option<GroupId>,
/// 消息的元数据
pub meta: Option<ReplyMeta>,
/// 额外携带的文件
#[serde(skip)]
pub file: SendingFile,
}
impl SendingMessage {
pub fn new(
content: String,
converse_id: ConverseId,
group_id: Option<GroupId>,
meta: Option<ReplyMeta>,
) -> Self {
Self {
content,
converse_id,
group_id,
meta,
file: SendingFile::None,
}
}
pub fn new_without_meta(
content: String,
converse_id: ConverseId,
group_id: Option<GroupId>,
) -> Self {
Self {
content,
converse_id,
group_id,
meta: None,
file: SendingFile::None,
}
}
pub fn contain_file(&self) -> bool { self.file.is_some() }
pub fn add_img(&mut self, file: SendingFile) { self.file = file; }
pub fn as_value(&self) -> JsonValue { serde_json::to_value(self).unwrap() }
}
#[derive(Debug, Clone)]
pub struct ReplyMeta {
/// 被回复的人的ID (可以是多个)
pub mentions: Vec<UserId>,
/// 被回复的消息ID
pub reply_id: MessageId,
/// 被回复的消息的发送者ID
pub reply_author: UserId,
/// 被回复的消息内容
pub reply_content: String,
}
impl ReplyMeta {
pub fn from_receive_message(msg: &ReceiveMessage) -> Self {
Self {
mentions: vec![msg.sender_id.clone()],
reply_id: msg.msg_id.clone(),
reply_author: msg.sender_id.clone(),
reply_content: msg.content.clone(),
}
}
pub fn add_mention(&mut self, user_id: UserId) { self.mentions.push(user_id); }
pub fn replace_content(&mut self, content: String) { self.reply_content = content; }
}
impl Serialize for ReplyMeta {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
let reply = json! {
{
"_id": self.reply_id,
"author": self.reply_author,
"content": self.reply_content,
}
};
let mut map = serde_json::Map::new();
map.insert("mentions".to_string(), serde_json::to_value(&self.mentions).unwrap());
map.insert("reply".to_string(), reply);
map.serialize(serializer)
}
}