1use crate::impl_str_enum;
2use crate::model::user::{Metal, to_metal};
3use crate::utils::error::Error;
4use serde::{Deserialize, Deserializer};
5use serde_json::Value;
6use std::str::FromStr;
7
8#[derive(Clone, Debug)]
9pub enum ClientType {
10 Web,
12 PC,
14 Mobile,
16 Windows,
18 MacOs,
20 Linux,
22 Ios,
24 Android,
26 Idea,
28 Chrome,
30 Edge,
32 VSCode,
34 Python,
36 Golang,
38 Rust,
40 Harmony,
42 Cli,
44 Bird,
46 IceNet,
48 ElvesOnline,
50 Other,
52}
53
54pub enum ChatContentType {
55 Markdown,
56 Html,
57}
58
59#[repr(u8)]
61pub enum ChatRoomMessageMode {
62 Context = 0,
64 Before = 1,
66 After = 2,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Hash)]
71pub enum ChatRoomMessageType {
72 Online,
74 DiscussChanged,
76 Revoke,
78 Msg,
80 RedPacket,
82 RedPacketStatus,
84 Barrager,
86 Custom,
88}
89
90#[derive(Clone, Debug)]
91pub struct CustomMsg {
92 pub message: String,
93}
94pub struct DiscussMsg;
95
96#[derive(Clone, Debug)]
97pub struct RevokeMsg {
98 pub msg: String,
99}
100
101#[derive(Clone, Debug)]
102pub struct BarragerCost {
103 pub cost: u32,
104 pub unit: String,
105}
106
107pub struct WeatherData {
116 pub date: String,
117 pub code: WeatherCode,
118 pub min: String,
119 pub max: String,
120}
121
122pub struct ChatRoomSource {
124 pub client: String,
125 pub version: String,
126}
127
128#[derive(Clone, Debug)]
129pub enum WeatherCode {
130 ClearDay,
131 ClearNight,
132 Cloudy,
133 Dust,
134 Fog,
135 HeavyHaze,
136 HeavyRain,
137 HeavySnow,
138 LightHaze,
139 LightRain,
140 LightSnow,
141 ModerateHaze,
142 ModerateRain,
143 ModerateSnow,
144 PartlyCloudyDay,
145 PartlyCloudyNight,
146 Sand,
147 StormRain,
148 StormSnow,
149 Wind,
150}
151
152#[derive(Clone, Debug)]
154#[allow(non_snake_case)]
155pub struct ChatRoomMsg<T = Value> {
156 pub r#type: ChatRoomMessageType,
157 pub oId: String,
158 pub time: String,
159 pub userOId: String,
160 pub userName: String,
161 pub userNickname: String,
162 pub userAvatarURL: String,
163 pub sysMetal: Vec<Metal>,
164 pub content: T,
165 pub md: String,
166 pub client: String,
167 pub via: ClientType,
168}
169
170#[derive(Clone, Debug, Deserialize)]
171#[allow(non_snake_case)]
172pub struct BarragerMsg {
173 pub userName: String,
175 pub userNickname: String,
177 pub barragerContent: String,
179 pub barragerColor: String,
181 pub userAvatarURL: String,
183 pub userAvatarURL200: String,
185 pub userAvatarURL48: String,
187 pub userAvatarURL210: String,
189}
190
191#[derive(Clone, Debug)]
193#[allow(non_snake_case)]
194pub struct OnlineInfo {
195 pub homePage: String,
197 pub userAvatarURL: String,
199 pub userName: String,
201}
202
203impl_str_enum!(ClientType {
204 Web => "Web",
205 PC => "PC",
206 Mobile => "Mobile",
207 Windows => "Windows",
208 MacOs => "macOS",
209 Linux => "Linux",
210 Ios => "iOS",
211 Android => "Android",
212 Idea => "IDEA",
213 Chrome => "Chrome",
214 Edge => "Edge",
215 VSCode => "VSCode",
216 Python => "Python",
217 Golang => "Golang",
218 Rust => "Rust",
219 Harmony => "Harmony",
220 Cli => "CLI",
221 Bird => "Bird",
222 IceNet => "IceNet",
223 ElvesOnline => "ElvesOnline",
224 Other => "Other",
225});
226
227impl_str_enum!(ChatContentType {
228 Markdown => "Markdown",
229 Html => "Html",
230});
231
232impl_str_enum!(ChatRoomMessageType {
233 Online => "online",
234 DiscussChanged => "discussChanged",
235 Revoke => "revoke",
236 Msg => "msg",
237 RedPacket => "redPacket",
238 RedPacketStatus => "redPacketStatus",
239 Barrager => "barrager",
240 Custom => "customMessage",
241});
242
243impl_str_enum!(WeatherCode {
244 ClearDay => "CLEAR_DAY",
245 ClearNight => "CLEAR_NIGHT",
246 Cloudy => "CLOUDY",
247 Dust => "DUST",
248 Fog => "FOG",
249 HeavyHaze => "HEAVY_HAZE",
250 HeavyRain => "HEAVY_RAIN",
251 HeavySnow => "HEAVY_SNOW",
252 LightHaze => "LIGHT_HAZE",
253 LightRain => "LIGHT_RAIN",
254 LightSnow => "LIGHT_SNOW",
255 ModerateHaze => "MODERATE_HAZE",
256 ModerateRain => "MODERATE_RAIN",
257 ModerateSnow => "MODERATE_SNOW",
258 PartlyCloudyDay => "PARTLY_CLOUDY_DAY",
259 PartlyCloudyNight => "PARTLY_CLOUDY_NIGHT",
260 Sand => "SAND",
261 StormRain => "STORM_RAIN",
262 StormSnow => "STORM_SNOW",
263 Wind => "WIND",
264});
265
266impl_str_enum!(ChatRoomMessageMode{
267 Context => "0",
268 Before => "1",
269 After => "2",
270});
271
272impl Default for ChatRoomSource {
273 fn default() -> Self {
274 Self {
275 client: "Other".to_string(),
276 version: "latest".to_string(),
277 }
278 }
279}
280
281impl ChatRoomMsg {
282 pub fn from_value(value: &Value) -> Result<Self, Error> {
283 serde_json::from_value(value.clone())
284 .map_err(|e| Error::Parse(format!("Failed to parse ChatRoomMsg: {}", e)))
285 }
286
287 pub fn name(&self) -> &str {
288 if self.userNickname.is_empty() {
289 &self.userName
290 } else {
291 &self.userNickname
292 }
293 }
294}
295
296impl BarragerMsg {
297 pub fn from_value(value: &Value) -> Result<Self, Error> {
298 serde_json::from_value(value.clone())
299 .map_err(|e| Error::Parse(format!("Failed to parse BarragerMsg: {}", e)))
300 }
301}
302
303fn parse_content(content: &str) -> (ChatRoomMessageType, Value) {
304 if let Ok(data) = serde_json::from_str::<Value>(content) {
305 if let Some(msg_type_str) = data["msgType"].as_str() {
306 match msg_type_str {
307 "redPacket" => (ChatRoomMessageType::RedPacket, data),
308 "music" => (ChatRoomMessageType::Msg, data),
309 "weather" => (ChatRoomMessageType::Msg, data),
310 _ => (ChatRoomMessageType::Msg, Value::String(content.to_string())),
311 }
312 } else {
313 (ChatRoomMessageType::Msg, Value::String(content.to_string()))
314 }
315 } else {
316 (ChatRoomMessageType::Msg, Value::String(content.to_string()))
317 }
318}
319
320impl<'de> Deserialize<'de> for ChatRoomMsg<Value> {
321 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
322 where
323 D: Deserializer<'de>,
324 {
325 #[derive(Deserialize)]
326 #[allow(non_snake_case)]
327 struct Raw {
328 oId: String,
329 time: String,
330 userOId: Value,
331 userName: String,
332 userNickname: String,
333 userAvatarURL: String,
334 sysMetal: Option<Value>,
335 content: String,
336 md: Option<String>,
337 client: Option<String>,
338 }
339
340 let raw = Raw::deserialize(deserializer)?;
341
342 let (r#type, content) = parse_content(&raw.content);
343
344 let via = raw
345 .client
346 .as_ref()
347 .and_then(|s| ClientType::from_str(s).ok())
348 .unwrap_or(ClientType::Other);
349 let client = raw.client.unwrap_or(ClientType::Rust.as_str().to_string());
350
351 let sys_metal = raw
352 .sysMetal
353 .as_ref()
354 .and_then(|v| v.as_str())
355 .map(|s| to_metal(s))
356 .unwrap_or(Ok(vec![]))
357 .unwrap_or(vec![]);
358
359 Ok(ChatRoomMsg {
360 r#type,
361 oId: raw.oId,
362 time: raw.time,
363 userOId: raw.userOId.to_string(),
364 userName: raw.userName,
365 userNickname: raw.userNickname,
366 userAvatarURL: raw.userAvatarURL,
367 sysMetal: sys_metal,
368 content,
369 md: raw.md.unwrap_or("".to_string()),
370 client,
371 via,
372 })
373 }
374}
375
376impl BarragerCost {
377 pub fn from_value(value: &Value) -> Self {
378 let content = value
379 .get("data")
380 .and_then(|v| v.as_str())
381 .unwrap_or("5积分");
382 let mut parts = content
383 .split(|c: char| !c.is_alphanumeric())
384 .filter(|s| !s.is_empty());
385 let cost = parts
386 .next()
387 .and_then(|s| s.parse::<u32>().ok())
388 .unwrap_or(0);
389 let unit = parts.next().unwrap_or("积分").to_string();
390
391 Self {
392 cost,
393 unit,
394 }
395 }
396}