Skip to main content

shirabe_core/
message.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Deserialize, Serialize)]
4#[serde(tag = "type", content = "data")]
5pub enum MessageElement {
6    /// 文本
7    #[serde(rename = "text")]
8    Text {
9        /// 消息文本
10        #[serde(rename = "content")]
11        text: String,
12    },
13    /// 提及用户
14    #[serde(rename = "at")]
15    At {
16        /// 目标用户的 ID
17        id: String,
18        /// 目标用户的名称
19        name: Option<String>,
20        /// 目标角色
21        role: Option<String>,
22        #[serde(rename = "type")]
23        at_type: Option<String>, // all 表示 @全体成员,here 表示 @在线成员
24    },
25
26    /// 提及频道
27    #[serde(rename = "sharp")]
28    Sharp {
29        /// 目标频道的 ID
30        id: String,
31        /// 目标频道的名称
32        name: Option<String>,
33    },
34
35    /// 图片
36    #[serde(rename = "img")]
37    Image {
38        /// 资源的 URL
39        src: String,
40        /// 资源的名称
41        title: Option<String>,
42        /// 图片宽度 (像素)
43        width: Option<u32>,
44        /// 图片高度 (像素)
45        height: Option<u32>,
46        /// 是否使用已缓存的文件
47        cache: Option<bool>,
48        /// 下载文件的最长时间 (毫秒)
49        timeout: Option<String>,
50    },
51    #[serde(rename = "audio")]
52    /// 音频
53    Audio {
54        /// 资源的 URL
55        src: String,
56        /// 资源的名称
57        title: Option<String>,
58        /// 音频长度 (秒)
59        duration: Option<u64>,
60        /// 音频封面 URL
61        poster: Option<String>,
62        /// 是否使用已缓存的文件
63        cache: Option<bool>,
64        /// 下载文件的最长时间 (毫秒)
65        timeout: Option<String>,
66    },
67    /// 视频
68    #[serde(rename = "video")]
69    Video {
70        /// 资源的 URL
71        src: String,
72        /// 资源的名称
73        title: Option<String>,
74        /// 视频长度 (秒)
75        duration: Option<u64>,
76        /// 视频封面 URL
77        poster: Option<String>,
78        /// 视频宽度 (像素)
79        width: Option<u32>,
80        /// 视频高度 (像素)
81        height: Option<u32>,
82        /// 是否使用已缓存的文件
83        cache: Option<bool>,
84        /// 下载文件的最长时间 (毫秒)
85        timeout: Option<String>,
86    },
87    /// 文件
88    #[serde(rename = "file")]
89    File {
90        /// 资源的 URL
91        src: String,
92        /// 资源的名称
93        name: Option<String>,
94        /// 缩略图封面 URL
95        poster: Option<String>,
96        /// 是否使用已缓存的文件
97        cache: Option<bool>,
98        /// 下载文件的最长时间 (毫秒)
99        timeout: Option<String>,
100    },
101    /// 引用
102    #[serde(rename = "quote")]
103    Quote {
104        /// 引用的消息ID
105        id: String,
106        /// 是否为合并转发
107        forward: Option<bool>,
108        /// 子元素
109        children: Vec<MessageElement>,
110    },
111    /// 作者
112    #[serde(rename = "author")]
113    Author {
114        /// 用户 ID
115        id: String,
116        /// 昵称
117        name: Option<String>,
118        /// 头像 URL
119        avatar: Option<String>,
120    },
121    /// 消息
122    #[serde(rename = "message")]
123    Message {
124        /// 消息的 ID
125        id: Option<String>,
126        /// 是否为转发消息
127        forward: Option<bool>,
128        /// 消息的子元素
129        children: Vec<MessageElement>,
130    },
131
132    // 修饰元素
133    /// 粗体
134    #[serde(rename = "strong")]
135    Bold { children: Vec<MessageElement> },
136    /// 斜体
137    #[serde(rename = "em")]
138    Italic { children: Vec<MessageElement> },
139    /// 下划线
140    #[serde(rename = "u")]
141    Underline { children: Vec<MessageElement> },
142    /// 删除线
143    #[serde(rename = "s")]
144    Strikethrough { children: Vec<MessageElement> },
145    /// 剧透
146    #[serde(rename = "spl")]
147    Spoiler { children: Vec<MessageElement> },
148    /// 代码
149    #[serde(rename = "code")]
150    Code { children: Vec<MessageElement> },
151    /// 上标
152    #[serde(rename = "sup")]
153    Superscript { children: Vec<MessageElement> },
154    /// 下标
155    #[serde(rename = "sub")]
156    Subscript { children: Vec<MessageElement> },
157
158    // 排版元素
159    /// 换行
160    #[serde(rename = "br")]
161    LineBreak,
162    /// 段落
163    #[serde(rename = "p")]
164    Paragraph { children: Vec<MessageElement> },
165    /// 链接
166    #[serde(rename = "a")]
167    Link {
168        /// 	链接的 URL
169        href: String,
170        children: Vec<MessageElement>,
171    },
172    // #[serde(rename = "hr")]
173    // HorizontalRule,
174
175    // 列表元素
176    #[serde(rename = "li")]
177    ListItem { children: Vec<MessageElement> },
178    #[serde(rename = "ul")]
179    UnorderedList { children: Vec<MessageElement> },
180    #[serde(rename = "ol")]
181    OrderedList {
182        start: Option<u64>,
183        reversed: Option<bool>,
184        #[serde(rename = "type")]
185        list_type: Option<String>, // e.g., "1", "a", "A", "i", "I"
186        children: Vec<MessageElement>,
187    },
188
189    // 表格元素
190    #[serde(rename = "table")]
191    Table { children: Vec<MessageElement> }, // children: thead?, tbody, tfoot?
192    #[serde(rename = "thead")]
193    TableHead { children: Vec<MessageElement> }, // children: tr
194    #[serde(rename = "tbody")]
195    TableBody { children: Vec<MessageElement> }, // children: tr
196    #[serde(rename = "tfoot")]
197    TableFoot { children: Vec<MessageElement> }, // children: tr
198    #[serde(rename = "tr")]
199    TableRow { children: Vec<MessageElement> }, // children: th | td
200    #[serde(rename = "th")]
201    TableHeader { children: Vec<MessageElement> },
202    #[serde(rename = "td")]
203    TableCell { children: Vec<MessageElement> },
204
205    // 交互元素
206    #[serde(rename = "button")]
207    Button {
208        /// 按钮的 ID
209        id: Option<String>,
210        /// 按钮的样式
211        theme: Option<String>, // e.g., "primary", "secondary", "success", "danger", "warning", "info"
212        /// 按钮的链接
213        href: Option<String>,
214        /// 按钮的文本
215        text: Option<String>,
216        /// 是否禁用按钮
217        disabled: Option<bool>,
218        children: Vec<MessageElement>,
219    },
220
221    // HTML-like 元素
222    #[serde(rename = "span")]
223    Span {
224        style: Option<String>,
225        children: Vec<MessageElement>,
226    },
227    #[serde(rename = "div")]
228    Div {
229        style: Option<String>,
230        children: Vec<MessageElement>,
231    },
232}
233
234#[cfg(test)]
235mod tests {
236    use super::*;
237
238    #[test]
239    fn test_serialize_complex_message() {
240        let msg = vec![
241            MessageElement::Author {
242                id: "user123".to_string(),
243                name: Some("Alice".to_string()),
244                avatar: Some("http://example.com/alice.png".to_string()),
245            },
246            MessageElement::Text {
247                text: "Hello, ".to_string(),
248            },
249            MessageElement::Bold {
250                children: vec![MessageElement::Text {
251                    text: "world".to_string(),
252                }],
253            },
254            MessageElement::Image {
255                src: "http://example.com/image.png".to_string(),
256                title: None,
257                width: Some(100),
258                height: Some(100),
259                cache: None,
260                timeout: None,
261            },
262        ];
263
264        let serialized = serde_json::to_string_pretty(&msg).unwrap();
265        println!("{}", serialized);
266    }
267
268    #[test]
269    fn test_serialize_quote_message() {
270        let quote_content = MessageElement::Quote {
271            id: "prev_msg_id_123".to_string(),
272            forward: None,
273            children: vec![
274                MessageElement::Author {
275                    id: "user456".to_string(),
276                    name: Some("Bob".to_string()),
277                    avatar: None,
278                },
279                MessageElement::Text {
280                    text: "This was the original message.".to_string(),
281                },
282            ],
283        };
284
285        let main_message = vec![
286            MessageElement::Text {
287                text: "Replying to Bob: ".to_string(),
288            },
289            quote_content,
290            MessageElement::Text {
291                text: "What do you think?".to_string(),
292            },
293        ];
294
295        let serialized = serde_json::to_string_pretty(&main_message).unwrap();
296        println!("{}", serialized);
297    }
298}