flow_bot/base/
extract.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4
5use crate::{
6    api::api_ext::ApiExt,
7    event::{
8        BotEvent, TypedEvent,
9        message::{GroupSenderInfo, PrivateSenderInfo, SenderSex},
10    },
11    message::{self, segments::Segment},
12};
13
14use super::context::BotContext;
15
16#[async_trait]
17/// Extractor trait for extracting information from BotEvent and BotContext.
18pub trait FromEvent {
19    async fn from_event(context: BotContext, event: BotEvent) -> Option<Self>
20    where
21        Self: Sized;
22}
23
24/// State extractor, extract the state from BotContext.
25/// If the required state is not found, the handler will be skipped.
26pub struct State<S> {
27    pub state: Arc<S>,
28}
29
30#[async_trait]
31impl<S> FromEvent for State<S>
32where
33    S: 'static + Send + Sync,
34{
35    async fn from_event(context: BotContext, _: BotEvent) -> Option<Self> {
36        let state = context.state.get::<S>()?;
37        Some(Self { state })
38    }
39}
40
41/// Extractor for message event.
42pub struct MessageBody {
43    pub message: message::Message,
44}
45
46#[async_trait]
47impl FromEvent for MessageBody {
48    async fn from_event(_: BotContext, event: BotEvent) -> Option<MessageBody> {
49        match event.event {
50            crate::event::TypedEvent::Message(ref msg) => Some(Self {
51                message: msg.message.clone(),
52            }),
53            _ => None,
54        }
55    }
56}
57
58unsafe impl Send for MessageBody {}
59unsafe impl Sync for MessageBody {}
60
61pub struct BasicSenderInfo {
62    pub user_id: Option<i64>,
63    pub nickname: Option<String>,
64    pub sex: Option<SenderSex>,
65    pub age: Option<i32>,
66}
67
68impl From<PrivateSenderInfo> for BasicSenderInfo {
69    fn from(info: PrivateSenderInfo) -> Self {
70        Self {
71            user_id: info.user_id,
72            nickname: info.nickname,
73            sex: info.sex,
74            age: info.age,
75        }
76    }
77}
78
79impl From<GroupSenderInfo> for BasicSenderInfo {
80    fn from(info: GroupSenderInfo) -> Self {
81        Self {
82            user_id: info.user_id,
83            nickname: info.nickname,
84            sex: info.sex,
85            age: info.age,
86        }
87    }
88}
89
90pub struct Sender {
91    pub info: BasicSenderInfo,
92}
93
94#[async_trait]
95impl FromEvent for Sender {
96    async fn from_event(_: BotContext, event: BotEvent) -> Option<Self> {
97        match event.event {
98            TypedEvent::Message(ref msg) => {
99                let info = match &msg.info {
100                    crate::event::message::TypedMessageInfo::Private(info) => {
101                        info.sender.clone().into()
102                    }
103                    crate::event::message::TypedMessageInfo::Group(info) => {
104                        info.sender.clone().into()
105                    }
106                };
107                Some(Self { info })
108            }
109            _ => None,
110        }
111    }
112}
113
114unsafe impl Send for Sender {}
115unsafe impl Sync for Sender {}
116
117pub struct At {
118    pub user_id: String,
119}
120
121#[async_trait]
122impl FromEvent for At {
123    async fn from_event(_: BotContext, event: BotEvent) -> Option<Self> {
124        if let TypedEvent::Message(ref msg) = event.event {
125            msg.message.iter().find_map(|seg| match seg {
126                Segment::At(at) => Some(Self {
127                    user_id: at.qq.clone(),
128                }),
129                _ => None,
130            })
131        } else {
132            None
133        }
134    }
135}
136
137unsafe impl Send for At {}
138unsafe impl Sync for At {}
139
140pub struct GroupId {
141    pub group_id: i64,
142}
143
144#[async_trait]
145impl FromEvent for GroupId {
146    async fn from_event(_: BotContext, event: BotEvent) -> Option<Self>
147    where
148        Self: Sized,
149    {
150        if let TypedEvent::Message(ref msg) = event.event {
151            match &msg.info {
152                crate::event::message::TypedMessageInfo::Group(info) => Some(Self {
153                    group_id: info.group_id,
154                }),
155                _ => None,
156            }
157        } else {
158            None
159        }
160    }
161}
162
163unsafe impl Send for GroupId {}
164unsafe impl Sync for GroupId {}
165
166pub struct SenderId {
167    pub user_id: i64,
168}
169
170#[async_trait]
171impl FromEvent for SenderId {
172    async fn from_event(context: BotContext, event: BotEvent) -> Option<Self>
173    where
174        Self: Sized,
175    {
176        let sender_info = Sender::from_event(context, event).await?;
177        Some(Self {
178            user_id: sender_info.info.user_id?,
179        })
180    }
181}
182
183unsafe impl Send for SenderId {}
184unsafe impl Sync for SenderId {}
185
186pub struct MatchGroupId<const ID: i64>;
187
188#[async_trait]
189impl<const ID: i64> FromEvent for MatchGroupId<ID> {
190    async fn from_event(context: BotContext, event: BotEvent) -> Option<Self>
191    where
192        Self: Sized,
193    {
194        let group_id = GroupId::from_event(context, event).await?.group_id;
195        if group_id == ID { Some(Self) } else { None }
196    }
197}
198
199unsafe impl<const ID: i64> Send for MatchGroupId<ID> {}
200unsafe impl<const ID: i64> Sync for MatchGroupId<ID> {}
201
202pub struct Reply {
203    pub reply: message::Message,
204}
205
206#[async_trait]
207impl FromEvent for Reply {
208    async fn from_event(context: BotContext, event: BotEvent) -> Option<Self>
209    where
210        Self: Sized,
211    {
212        if let TypedEvent::Message(ref msg) = event.event {
213            for segment in msg.message.iter() {
214                if let Segment::Reply(reply) = segment {
215                    let id = reply.id.parse::<i64>().ok()?;
216                    let message = context
217                        .get_message(id)
218                        .await
219                        .map(|msg| msg.message.clone())
220                        .ok()?;
221                    return Some(Self { reply: message });
222                }
223            }
224        }
225        None
226    }
227}
228
229#[async_trait]
230impl<T> FromEvent for Option<T>
231where
232    T: FromEvent,
233{
234    async fn from_event(context: BotContext, event: BotEvent) -> Option<Self>
235    where
236        Self: Sized,
237    {
238        Some(T::from_event(context, event).await)
239    }
240}
241
242/// A helper macro for matching one of the variants.
243/// The macro will generate an enum with the given name and variants.
244/// The enum will implement the FromEvent trait, and will try to match the event with the given matchers.
245///
246/// # Example
247/// ```no_run
248/// match_one!(MatchOne, A: AMatcher, B: BMatcher);
249/// ```
250/// The above code will generate an enum like this:
251/// ```no_run
252/// pub enum MatchOne {
253///    A(AMatcher),
254///    B(BMatcher),
255/// }
256/// ```
257#[macro_export]
258macro_rules! match_one {
259    ($name:ident,$($variant:ident : $matcher:ty),*) => {
260        pub enum $name {
261            $(
262                $variant($matcher),
263            )*
264        }
265
266        #[async_trait::async_trait]
267        impl $crate::base::extract::FromEvent for $name {
268            async fn from_event(context: $crate::base::context::BotContext, event: $crate::event::BotEvent) -> Option<Self> {
269                $(
270                    if let Some(matcher) = <$matcher>::from_event(context.clone(), event.clone()).await {
271                        return Some(Self::$variant(matcher));
272                    }
273                )*
274                None
275            }
276        }
277    };
278}