Skip to main content

kovi_onebot/event/
msg_event.rs

1use super::{Anonymous, Sender};
2use crate::event::{PostType, RepliableEvent, Sex, UniversalMessage};
3use crate::message_trait::MessageRegistrar as _;
4use crate::onebot_message::{OneBotMessage, cq_to_arr_inner};
5use kovi::bot::runtimebot::{CanSendApi, send_api_request_with_forget};
6use kovi::bot::{BotInformation, SendApi};
7use kovi::error::EventBuildError;
8use kovi::event::id::ref_id::RefID;
9use kovi::event::{Event, InternalEvent, MessageEventTrait};
10use kovi::message::Message as KoviMessage;
11use kovi::types::ApiAndOptOneshot;
12use log::{debug, info};
13use serde::Serialize;
14use serde_json::value::Index;
15use serde_json::{self, Value, json};
16use tokio::sync::mpsc;
17
18#[derive(Debug, Clone)]
19pub struct MsgEvent {
20    /// 事件发生的时间戳
21    pub time: i64,
22    /// 收到事件的机器人 登陆号
23    pub self_id: i64,
24    /// 上报类型
25    pub post_type: PostType,
26    /// 消息类型
27    pub message_type: String,
28    /// 消息子类型,如果是好友则是 friend,如果是群临时会话则是 group
29    pub sub_type: String,
30    /// 消息内容
31    pub message: KoviMessage,
32    /// 消息 ID
33    pub message_id: i32,
34    /// 群号
35    pub group_id: Option<i64>,
36    /// 发送者号
37    pub user_id: i64,
38    /// 匿名信息,如果不是匿名消息则为 null
39    pub anonymous: Option<Anonymous>,
40    /// 原始消息内容
41    pub raw_message: String,
42    /// 字体
43    pub font: i32,
44    /// 发送人信息
45    pub sender: Sender,
46
47    /// 处理过的纯文本,如果是纯图片或无文本,此处为None
48    pub text: Option<String>,
49    /// 处理过的文本,会解析成人类易读形式,里面会包含\[image\]\[face\]等解析后字符串
50    pub human_text: String,
51    /// 原始的onebot消息,已处理成json格式
52    pub original_json: Value,
53
54    /// 不推荐的消息发送方式
55    pub api_tx: mpsc::Sender<ApiAndOptOneshot>,
56}
57
58impl MessageEventTrait for MsgEvent {
59    fn get_sender_name(&self) -> Option<&str> {
60        self.sender.nickname.as_deref()
61    }
62
63    fn get_message(&self) -> &KoviMessage {
64        &self.message
65    }
66
67    fn get_message_type_str(&self) -> Option<&str> {
68        Some(&self.message_type)
69    }
70
71    fn get_sender_id(&self) -> RefID<'_> {
72        RefID::new(&self.sender.user_id)
73    }
74
75    fn get_group_id(&self) -> Option<RefID<'_>> {
76        self.group_id.as_ref().map(RefID::new)
77    }
78}
79
80impl Event for MsgEvent {
81    fn de(
82        event: &InternalEvent,
83        _: &BotInformation,
84        api_tx: &mpsc::Sender<ApiAndOptOneshot>,
85    ) -> Option<Self> {
86        let InternalEvent::DriverEvent(json) = event else {
87            return None;
88        };
89
90        Self::new(api_tx.clone(), json.clone()).ok()
91    }
92}
93
94impl MsgEvent {
95    pub(crate) fn new(
96        api_tx: mpsc::Sender<ApiAndOptOneshot>,
97        temp: Value,
98    ) -> Result<MsgEvent, EventBuildError> {
99        let temp_object = temp.as_object().ok_or(EventBuildError::ParseError(
100            "Invalid JSON object".to_string(),
101        ))?;
102
103        let temp_sender = temp_object
104            .get("sender")
105            .and_then(|v| v.as_object())
106            .ok_or(EventBuildError::ParseError(
107                "Invalid sender object".to_string(),
108            ))?;
109
110        let sender = {
111            Sender {
112                user_id: temp_sender
113                    .get("user_id")
114                    .and_then(|v| v.as_i64())
115                    .ok_or(EventBuildError::ParseError("Invalid user_id".to_string()))?,
116                nickname: temp_sender.get("nickname").and_then(|v| {
117                    if let Value::String(str) = v.clone() {
118                        Some(str)
119                    } else {
120                        None
121                    }
122                }),
123                card: temp_sender.get("card").and_then(|v| {
124                    if let Value::String(str) = v.clone() {
125                        Some(str)
126                    } else {
127                        None
128                    }
129                }),
130                sex: if let Some(v) = temp_sender.get("sex").and_then(|v| v.as_str()) {
131                    match v {
132                        "male" => Some(Sex::Male),
133                        "female" => Some(Sex::Female),
134                        _ => None,
135                    }
136                } else {
137                    None
138                },
139                age: temp_sender
140                    .get("age")
141                    .and_then(|v| v.as_i64())
142                    .map(|v| v as i32),
143                area: temp_sender.get("area").and_then(|v| {
144                    if let Value::String(str) = v.clone() {
145                        Some(str)
146                    } else {
147                        None
148                    }
149                }),
150                level: temp_sender.get("level").and_then(|v| {
151                    if let Value::String(str) = v.clone() {
152                        Some(str)
153                    } else {
154                        None
155                    }
156                }),
157                role: temp_sender.get("role").and_then(|v| {
158                    if let Value::String(str) = v.clone() {
159                        Some(str)
160                    } else {
161                        None
162                    }
163                }),
164                title: temp_sender.get("title").and_then(|v| {
165                    if let Value::String(str) = v.clone() {
166                        Some(str)
167                    } else {
168                        None
169                    }
170                }),
171            }
172        };
173
174        let group_id = temp_object.get("group_id").and_then(|v| v.as_i64());
175
176        let message = if temp_object
177            .get("message")
178            .and_then(|v| v.as_array())
179            .is_some()
180        {
181            let v = temp_object
182                .get("message")
183                .ok_or(EventBuildError::ParseError(
184                    "Missing 'message' field".to_string(),
185                ))?
186                .as_array()
187                .ok_or(EventBuildError::ParseError(
188                    "Invalid 'message' array".to_string(),
189                ))?
190                .to_vec();
191            OneBotMessage::from_vec_segment_value(v)
192                .map_err(|e| EventBuildError::ParseError(format!("Parse error: {e}")))?
193        } else {
194            let str_v = temp_object["message"]
195                .as_str()
196                .ok_or(format!(
197                    "message is not string:{:?}",
198                    temp_object["message"]
199                ))
200                .map_err(|e| EventBuildError::ParseError(format!("Parse error: {e}")))?;
201            let arr_v = cq_to_arr_inner(str_v);
202            OneBotMessage::from_vec_segment_value(arr_v)
203                .map_err(|e| EventBuildError::ParseError(format!("Parse error: {e}")))?
204        };
205
206        let anonymous: Option<Anonymous> =
207            if temp_object.get("anonymous").is_none_or(|v| v.is_null()) {
208                None
209            } else {
210                let anonymous = temp_object
211                    .get("anonymous")
212                    .ok_or(EventBuildError::ParseError(
213                        "Invalid anonymous field".to_string(),
214                    ))?
215                    .clone();
216                Some(
217                    serde_json::from_value(anonymous)
218                        .map_err(|e| EventBuildError::ParseError(e.to_string()))?,
219                )
220            };
221
222        let text = {
223            let mut text_vec = Vec::new();
224            for msg in message.iter() {
225                if msg.type_ == "text"
226                    && let Some(text_value) = msg.data.get("text").and_then(|v| v.as_str())
227                {
228                    text_vec.push(text_value);
229                };
230            }
231            if !text_vec.is_empty() {
232                Some(text_vec.join("\n").trim().to_string())
233            } else {
234                None
235            }
236        };
237
238        let message = KoviMessage::from(message);
239        let event = MsgEvent {
240            human_text: message.to_human_string(),
241            time: temp_object
242                .get("time")
243                .and_then(|v| v.as_i64())
244                .ok_or(EventBuildError::ParseError("Invalid time".to_string()))?,
245            self_id: temp_object
246                .get("self_id")
247                .and_then(|v| v.as_i64())
248                .ok_or(EventBuildError::ParseError("Invalid self_id".to_string()))?,
249            post_type: temp_object
250                .get("post_type")
251                .and_then(|v| serde_json::from_value::<PostType>(v.clone()).ok())
252                .ok_or(EventBuildError::ParseError("Invalid post_type".to_string()))?,
253            message_type: temp_object
254                .get("message_type")
255                .and_then(|v| {
256                    if let Value::String(str) = v.clone() {
257                        Some(str)
258                    } else {
259                        None
260                    }
261                })
262                .ok_or(EventBuildError::ParseError(
263                    "Invalid message_type".to_string(),
264                ))?,
265            sub_type: temp_object
266                .get("sub_type")
267                .and_then(|v| {
268                    if let Value::String(str) = v.clone() {
269                        Some(str)
270                    } else {
271                        None
272                    }
273                })
274                .ok_or(EventBuildError::ParseError("Invalid sub_type".to_string()))?,
275            message,
276            message_id: temp_object
277                .get("message_id")
278                .and_then(|v| v.as_i64())
279                .ok_or(EventBuildError::ParseError(
280                    "Invalid message_id".to_string(),
281                ))? as i32,
282            group_id,
283            user_id: temp_object
284                .get("user_id")
285                .and_then(|v| v.as_i64())
286                .ok_or(EventBuildError::ParseError("Invalid user_id".to_string()))?,
287            anonymous,
288            raw_message: temp_object
289                .get("raw_message")
290                .and_then(|v| {
291                    if let Value::String(str) = v.clone() {
292                        Some(str)
293                    } else {
294                        None
295                    }
296                })
297                .ok_or(EventBuildError::ParseError(
298                    "Invalid raw_message".to_string(),
299                ))?,
300            font: temp_object
301                .get("font")
302                .and_then(|v| v.as_i64())
303                .ok_or(EventBuildError::ParseError("Invalid font".to_string()))?
304                as i32,
305            sender,
306            api_tx,
307            text,
308            original_json: temp,
309        };
310        debug!("{event:?}");
311        Ok(event)
312    }
313}
314
315impl MsgEvent {
316    /// 直接从原始的 Json Value 获取某值
317    ///
318    /// # example
319    ///
320    /// ```ignore
321    /// use kovi::PluginBuilder;
322    ///
323    /// PluginBuilder::on_msg(|event| async move {
324    ///     let time = event.get("time").and_then(|v| v.as_i64()).unwrap();
325    ///
326    ///     assert_eq!(time, event.time);
327    /// });
328    /// ```
329    pub fn get<I: Index>(&self, index: I) -> Option<&Value> {
330        self.original_json.get(index)
331    }
332}
333
334impl<I> std::ops::Index<I> for MsgEvent
335where
336    I: Index,
337{
338    type Output = Value;
339
340    fn index(&self, index: I) -> &Self::Output {
341        &self.original_json[index]
342    }
343}
344
345impl MsgEvent {
346    fn reply_builder<M>(&self, msg: M, auto_escape: bool) -> SendApi
347    where
348        M: Into<OneBotMessage>,
349    {
350        RepliableEvent::reply_builder(self, msg, auto_escape)
351    }
352
353    #[cfg(not(feature = "cqstring"))]
354    pub fn reply<T>(&self, msg: T)
355    where
356        KoviMessage: From<T>,
357        T: Serialize,
358    {
359        RepliableEvent::reply(self, msg)
360    }
361
362    #[cfg(feature = "cqstring")]
363    pub fn reply<T>(&self, msg: T)
364    where
365        CQMessage: From<T>,
366        T: Serialize,
367    {
368        RepliableEvent::reply(self, msg)
369    }
370
371    #[cfg(not(feature = "cqstring"))]
372    pub fn reply_and_quote<T>(&self, msg: T)
373    where
374        KoviMessage: From<T>,
375        T: Serialize,
376    {
377        RepliableEvent::reply_and_quote(self, msg);
378    }
379
380    #[cfg(feature = "cqstring")]
381    fn reply_and_quote<T>(&self, msg: T)
382    where
383        CQMessage: From<T>,
384        T: Serialize,
385    {
386        RepliableEvent::reply_and_quote(self, msg);
387    }
388
389    #[cfg(feature = "cqstring")]
390    fn reply_text<T>(&self, msg: T)
391    where
392        String: From<T>,
393        T: Serialize,
394    {
395        RepliableEvent::reply_text(self, msg)
396    }
397
398    pub fn get_text(&self) -> String {
399        RepliableEvent::get_text(self)
400    }
401
402    pub fn get_sender_nickname(&self) -> String {
403        RepliableEvent::get_sender_nickname(self)
404    }
405
406    pub fn borrow_text(&self) -> Option<&str> {
407        RepliableEvent::borrow_text(self)
408    }
409
410    pub fn is_group(&self) -> bool {
411        UniversalMessage::is_group(self)
412    }
413
414    pub fn is_private(&self) -> bool {
415        UniversalMessage::is_private(self)
416    }
417}
418
419impl UniversalMessage for MsgEvent {
420    fn is_group(&self) -> bool {
421        self.group_id.is_some()
422    }
423
424    fn is_private(&self) -> bool {
425        self.group_id.is_none()
426    }
427}
428
429impl RepliableEvent for MsgEvent {
430    fn reply_builder<M>(&self, msg: M, auto_escape: bool) -> SendApi
431    where
432        M: Into<OneBotMessage>,
433    {
434        let msg = msg.into();
435        if self.is_private() {
436            SendApi::new(
437                "send_msg",
438                json!({
439                    "message_type":"private",
440                "user_id":self.user_id,
441                "message":msg,
442                "auto_escape":auto_escape,
443                }),
444            )
445        } else {
446            SendApi::new(
447                "send_msg",
448                json!({
449                    "message_type":"group",
450                    "group_id":self.group_id.as_ref().expect("unreachable"),
451                    "message":msg,
452                    "auto_escape":auto_escape,
453                }),
454            )
455        }
456    }
457
458    #[cfg(not(feature = "cqstring"))]
459    /// 快速回复消息
460    fn reply<T>(&self, msg: T)
461    where
462        KoviMessage: From<T>,
463        T: Serialize,
464    {
465        let msg = KoviMessage::from(msg);
466        let mut nickname = self.get_sender_nickname();
467        nickname.insert(0, ' ');
468        let id = &self.sender.user_id;
469        let message_type = &self.message_type;
470        let group_id = match &self.group_id {
471            Some(v) => format!(" {v}"),
472            None => "".to_string(),
473        };
474        let human_msg = msg.to_human_string();
475        info!("[reply] [to {message_type}{group_id}{nickname} {id}]: {human_msg}");
476
477        let send_msg = self.reply_builder(msg, false);
478        send_api_request_with_forget(&self.api_tx, send_msg)
479    }
480
481    #[cfg(feature = "cqstring")]
482    /// 快速回复消息
483    fn reply<T>(&self, msg: T)
484    where
485        CQMessage: From<T>,
486        T: Serialize,
487    {
488        let msg = CQMessage::from(msg);
489        let send_msg = self.reply_builder(&msg, false);
490        let mut nickname = self.get_sender_nickname();
491        nickname.insert(0, ' ');
492        let id = &self.sender.user_id;
493        let message_type = &self.message_type;
494        let group_id = match &self.group_id {
495            Some(v) => format!(" {v}"),
496            None => "".to_string(),
497        };
498        let human_msg = Message::from(msg).to_human_string();
499        info!("[reply] [to {message_type}{group_id}{nickname} {id}]: {human_msg}");
500        send_api_request_with_forget(&self.api_tx, send_msg);
501    }
502
503    #[cfg(not(feature = "cqstring"))]
504    /// 快速回复消息并且**引用**
505    fn reply_and_quote<T>(&self, msg: T)
506    where
507        KoviMessage: From<T>,
508        T: Serialize,
509    {
510        let msg = KoviMessage::from(msg).add_reply(self.message_id);
511        let mut nickname = self.get_sender_nickname();
512        nickname.insert(0, ' ');
513        let id = &self.sender.user_id;
514        let message_type = &self.message_type;
515        let group_id = match &self.group_id {
516            Some(v) => format!(" {v}"),
517            None => "".to_string(),
518        };
519        let human_msg = msg.to_human_string();
520        info!("[reply] [to {message_type}{group_id}{nickname} {id}]: {human_msg}");
521
522        let send_msg = self.reply_builder(msg, false);
523        send_api_request_with_forget(&self.api_tx, send_msg);
524    }
525
526    #[cfg(feature = "cqstring")]
527    /// 快速回复消息并且**引用**
528    fn reply_and_quote<T>(&self, msg: T)
529    where
530        CQMessage: From<T>,
531        T: Serialize,
532    {
533        let msg = CQMessage::from(msg).add_reply(self.message_id);
534        let send_msg = self.reply_builder(&msg, false);
535
536        let mut nickname = self.get_sender_nickname();
537        nickname.insert(0, ' ');
538        let id = &self.sender.user_id;
539        let message_type = &self.message_type;
540        let group_id = match &self.group_id {
541            Some(v) => format!(" {v}"),
542            None => "".to_string(),
543        };
544        let human_msg = Message::from(msg).to_human_string();
545        info!("[reply] [to {message_type}{group_id}{nickname} {id}]: {human_msg}");
546        send_api_request_with_forget(&self.api_tx, send_msg);
547    }
548
549    #[cfg(feature = "cqstring")]
550    /// 快速回复消息,并且**kovi不进行解析,直接发送此字符串**
551    fn reply_text<T>(&self, msg: T)
552    where
553        String: From<T>,
554        T: Serialize,
555    {
556        let send_msg = self.reply_builder(&msg, true);
557        let mut nickname = self.get_sender_nickname();
558        nickname.insert(0, ' ');
559        let id = &self.sender.user_id;
560        let message_type = &self.message_type;
561        let group_id = match &self.group_id {
562            Some(v) => format!(" {v}"),
563            None => "".to_string(),
564        };
565        let msg = String::from(msg);
566        info!("[reply] [to {message_type}{group_id} {nickname} {id}]: {msg}");
567        send_api_request_with_forget(&self.api_tx, send_msg);
568    }
569
570    /// 便捷获取文本,如果没有文本则会返回空字符串,如果只需要借用,请使用 `borrow_text()`
571    fn get_text(&self) -> String {
572        match self.text.clone() {
573            Some(v) => v,
574            None => "".to_string(),
575        }
576    }
577
578    /// 便捷获取发送者昵称,如果无名字,此处为空字符串
579    fn get_sender_nickname(&self) -> String {
580        if let Some(v) = &self.sender.nickname {
581            v.clone()
582        } else {
583            "".to_string()
584        }
585    }
586
587    /// 借用 event 的 text,只是做了一下self.text.as_deref()的包装
588    fn borrow_text(&self) -> Option<&str> {
589        self.text.as_deref()
590    }
591}
592
593impl CanSendApi for MsgEvent {
594    fn __get_api_tx(&self) -> &tokio::sync::mpsc::Sender<kovi::types::ApiAndOptOneshot> {
595        &self.api_tx
596    }
597}