1use std::{ops::Deref, sync::Arc};
2
3use async_trait::async_trait;
4
5use crate::{
6 api::api_ext::ApiExt,
7 event::{
8 BotEvent, TypedEvent,
9 message::{
10 GroupSenderInfo, GroupSenderRole, PrivateSenderInfo, SenderSex, TypedMessageInfo,
11 },
12 },
13 message::{self, message_ext::MessageExt, segments::Segment},
14};
15
16use super::context::BotContext;
17
18#[async_trait]
19pub trait FromEvent {
21 async fn from_event(context: BotContext, event: BotEvent) -> Option<Self>
22 where
23 Self: Sized;
24}
25
26pub struct State<S>(pub Arc<S>);
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
41impl<S> Deref for State<S> {
42 type Target = Arc<S>;
43
44 fn deref(&self) -> &Self::Target {
45 &self.0
46 }
47}
48
49pub struct MessageBody(pub message::Message);
51
52#[async_trait]
53impl FromEvent for MessageBody {
54 async fn from_event(_: BotContext, event: BotEvent) -> Option<MessageBody> {
55 match event.event {
56 TypedEvent::Message(ref msg) => Some(Self(msg.message.clone())),
57 _ => None,
58 }
59 }
60}
61
62#[async_trait]
63impl FromEvent for GroupSenderRole {
64 async fn from_event(_: BotContext, event: BotEvent) -> Option<Self> {
65 match event.event {
66 TypedEvent::Message(ref msg) => match &msg.info {
67 TypedMessageInfo::Group(info) => info.sender.role.clone(),
68 _ => None,
69 },
70 _ => None,
71 }
72 }
73}
74
75#[async_trait]
76impl FromEvent for GroupSenderInfo {
77 async fn from_event(_: BotContext, event: BotEvent) -> Option<Self> {
78 match event.event {
79 TypedEvent::Message(ref msg) => match &msg.info {
80 TypedMessageInfo::Group(info) => Some(info.sender.clone()),
81 _ => None,
82 },
83 _ => None,
84 }
85 }
86}
87
88#[async_trait]
89impl FromEvent for PrivateSenderInfo {
90 async fn from_event(_: BotContext, event: BotEvent) -> Option<Self> {
91 match event.event {
92 TypedEvent::Message(ref msg) => match &msg.info {
93 TypedMessageInfo::Private(info) => Some(info.sender.clone()),
94 _ => None,
95 },
96 _ => None,
97 }
98 }
99}
100
101pub struct BasicSenderInfo {
102 pub user_id: Option<i64>,
103 pub nickname: Option<String>,
104 pub sex: Option<SenderSex>,
105 pub age: Option<i32>,
106}
107
108impl From<PrivateSenderInfo> for BasicSenderInfo {
109 fn from(info: PrivateSenderInfo) -> Self {
110 Self {
111 user_id: info.user_id,
112 nickname: info.nickname,
113 sex: info.sex,
114 age: info.age,
115 }
116 }
117}
118
119impl From<GroupSenderInfo> for BasicSenderInfo {
120 fn from(info: GroupSenderInfo) -> Self {
121 Self {
122 user_id: info.user_id,
123 nickname: info.nickname,
124 sex: info.sex,
125 age: info.age,
126 }
127 }
128}
129
130pub struct Sender(pub BasicSenderInfo);
131
132#[async_trait]
133impl FromEvent for Sender {
134 async fn from_event(_: BotContext, event: BotEvent) -> Option<Self> {
135 match event.event {
136 TypedEvent::Message(ref msg) => {
137 let info = match &msg.info {
138 TypedMessageInfo::Private(info) => info.sender.clone().into(),
139 TypedMessageInfo::Group(info) => info.sender.clone().into(),
140 };
141 Some(Self(info))
142 }
143 _ => None,
144 }
145 }
146}
147
148pub struct At(pub String);
149
150#[async_trait]
151impl FromEvent for At {
152 async fn from_event(_: BotContext, event: BotEvent) -> Option<Self> {
153 if let TypedEvent::Message(ref msg) = event.event {
154 msg.message.iter().find_map(|seg| match seg {
155 Segment::At(at) => Some(Self(at.qq.clone())),
156 _ => None,
157 })
158 } else {
159 None
160 }
161 }
162}
163
164pub struct GroupId(pub i64);
165
166#[async_trait]
167impl FromEvent for GroupId {
168 async fn from_event(_: BotContext, event: BotEvent) -> Option<Self>
169 where
170 Self: Sized,
171 {
172 if let TypedEvent::Message(ref msg) = event.event {
173 match &msg.info {
174 TypedMessageInfo::Group(info) => Some(Self(info.group_id)),
175 _ => None,
176 }
177 } else {
178 None
179 }
180 }
181}
182
183pub struct SenderId(pub i64);
184
185#[async_trait]
186impl FromEvent for SenderId {
187 async fn from_event(context: BotContext, event: BotEvent) -> Option<Self>
188 where
189 Self: Sized,
190 {
191 let sender_info = Sender::from_event(context, event).await?;
192 Some(Self(sender_info.0.user_id?))
193 }
194}
195
196pub struct MatchGroupId<const ID: i64>;
197
198#[async_trait]
199impl<const ID: i64> FromEvent for MatchGroupId<ID> {
200 async fn from_event(context: BotContext, event: BotEvent) -> Option<Self>
201 where
202 Self: Sized,
203 {
204 let group_id = GroupId::from_event(context, event).await?.0;
205 if group_id == ID { Some(Self) } else { None }
206 }
207}
208
209pub struct Reply(pub message::Message);
210
211#[async_trait]
212impl FromEvent for Reply {
213 async fn from_event(context: BotContext, event: BotEvent) -> Option<Self>
214 where
215 Self: Sized,
216 {
217 if let TypedEvent::Message(ref msg) = event.event {
218 for segment in msg.message.iter() {
219 if let Segment::Reply(reply) = segment {
220 let id = reply.id.parse::<i64>().ok()?;
221 let message = context
222 .get_message(id)
223 .await
224 .map(|msg| msg.message.clone())
225 .ok()?;
226 return Some(Self(message));
227 }
228 }
229 }
230 None
231 }
232}
233
234pub struct Command<const PREFIX: &'static str, Cmd> {
235 pub command: Cmd,
236}
237
238#[async_trait]
239impl<const PREFIX: &'static str, Cmd> FromEvent for Command<PREFIX, Cmd>
240where
241 Cmd: clap::Parser,
242{
243 async fn from_event(context: BotContext, event: BotEvent) -> Option<Self>
244 where
245 Self: Sized,
246 {
247 let message_body = MessageBody::from_event(context, event).await?;
248 let plain_text = message_body.0.extract_if_plain_text()?;
249 let trimmed = plain_text.trim();
250
251 if trimmed.split_whitespace().next() != Some(PREFIX) {
252 return None;
253 }
254
255 let cmd = Cmd::try_parse_from(trimmed.split_whitespace()).ok()?;
256 Some(Self { command: cmd })
257 }
258}
259
260#[async_trait]
261impl<T> FromEvent for Option<T>
262where
263 T: FromEvent,
264{
265 async fn from_event(context: BotContext, event: BotEvent) -> Option<Self>
266 where
267 Self: Sized,
268 {
269 Some(T::from_event(context, event).await)
270 }
271}
272
273#[macro_export]
289macro_rules! match_one {
290 ($name:ident,$($variant:ident : $matcher:ty),*) => {
291 pub enum $name {
292 $(
293 $variant($matcher),
294 )*
295 }
296
297 #[async_trait::async_trait]
298 impl $crate::base::extract::FromEvent for $name {
299 async fn from_event(context: $crate::base::context::BotContext, event: $crate::event::BotEvent) -> Option<Self> {
300 $(
301 if let Some(matcher) = <$matcher>::from_event(context.clone(), event.clone()).await {
302 return Some(Self::$variant(matcher));
303 }
304 )*
305 None
306 }
307 }
308 };
309}