Skip to main content

vk_bot_api/
handler.rs

1use crate::api::VkApi;
2use crate::error::VkResult;
3use crate::keyboard::Keyboard;
4use crate::models::Event;
5use async_trait::async_trait;
6
7#[async_trait]
8pub trait MessageHandler: Send + Sync {
9    async fn handle(&self, event: &Event, api: &VkApi) -> VkResult<()>;
10}
11
12#[derive(Debug, Clone, Default)]
13pub struct DefaultMessageHandler;
14
15#[async_trait]
16impl MessageHandler for DefaultMessageHandler {
17    async fn handle(&self, event: &Event, api: &VkApi) -> VkResult<()> {
18        match event {
19            Event::MessageNew(message) => {
20                let _ = api
21                    .messages_mark_as_read(message.peer_id, Some(message.id))
22                    .await;
23
24                if message.text.starts_with('/') {
25                    self.handle_command(message, api).await?;
26                } else {
27                    self.handle_text(message, api).await?;
28                }
29
30                if !message.attachments.is_empty() {
31                    self.handle_attachments(message, api).await?;
32                }
33
34                if !message.fwd_messages.is_empty() {
35                    self.handle_forwarded(message, api).await?;
36                }
37            }
38
39            Event::MessageEdit(message) => {
40                self.handle_edit(message, api).await?;
41            }
42
43            Event::MessageEvent(event) => {
44                self.handle_message_event(event, api).await?;
45            }
46
47            Event::MessageTypingState(state) => {
48                self.handle_typing(state, api).await?;
49            }
50
51            Event::MessageAllow(allow) => {
52                self.handle_message_allow(allow, api).await?;
53            }
54
55            _ => {}
56        }
57
58        Ok(())
59    }
60}
61
62impl DefaultMessageHandler {
63    async fn handle_command(&self, message: &crate::models::Message, api: &VkApi) -> VkResult<()> {
64        let command = message.text.split_whitespace().next().unwrap_or("");
65
66        match command {
67            "/start" | "/help" => {
68                let keyboard = Keyboard::create_menu();
69                api.messages_send(
70                    message.peer_id,
71                    "šŸ‘‹ Welcome! I'm a VK bot built with Rust.\n\nCommands:\n/start - Welcome message\n/help - Show help\n/menu - Show menu\n/info - Bot info",
72                    Some(&keyboard),
73                    None,
74                    None,
75                    None,
76                    None,
77                    false,
78                    false,
79                    None,
80                ).await?;
81            }
82
83            "/menu" => {
84                let keyboard = Keyboard::create_menu();
85                api.messages_send(
86                    message.peer_id,
87                    "šŸ“± Menu:",
88                    Some(&keyboard),
89                    None,
90                    None,
91                    None,
92                    None,
93                    false,
94                    false,
95                    None,
96                )
97                .await?;
98            }
99
100            "/info" => {
101                api.messages_send(
102                    message.peer_id,
103                    "šŸ¤– Bot built with vk-bot-api\nšŸ¦€ Powered by Rust\n⚔ Async/await\nšŸ”§ Full VK API support",
104                    None,
105                    None,
106                    None,
107                    None,
108                    None,
109                    false,
110                    false,
111                    None,
112                ).await?;
113            }
114
115            _ => {
116                api.messages_send(
117                    message.peer_id,
118                    "āŒ Unknown command. Use /help",
119                    None,
120                    None,
121                    None,
122                    None,
123                    None,
124                    false,
125                    false,
126                    None,
127                )
128                .await?;
129            }
130        }
131
132        Ok(())
133    }
134
135    async fn handle_text(&self, message: &crate::models::Message, api: &VkApi) -> VkResult<()> {
136        let response = format!("You said: {}", message.text);
137
138        api.messages_send(
139            message.peer_id,
140            &response,
141            None,
142            None,
143            None,
144            Some(message.id),
145            None,
146            false,
147            false,
148            None,
149        )
150        .await?;
151
152        Ok(())
153    }
154
155    async fn handle_attachments(
156        &self,
157        message: &crate::models::Message,
158        api: &VkApi,
159    ) -> VkResult<()> {
160        let mut types = Vec::new();
161
162        for attachment in &message.attachments {
163            match attachment.attachment_type.as_str() {
164                "photo" => types.push("šŸ“· Photo"),
165                "video" => types.push("šŸŽ„ Video"),
166                "audio" => types.push("šŸŽµ Audio"),
167                "doc" => types.push("šŸ“Ž Document"),
168                "sticker" => types.push("😊 Sticker"),
169                _ => types.push("šŸ“Ž Attachment"),
170            }
171        }
172
173        if !types.is_empty() {
174            let text = format!("Received attachments: {}", types.join(", "));
175
176            api.messages_send(
177                message.peer_id,
178                &text,
179                None,
180                None,
181                None,
182                Some(message.id),
183                None,
184                false,
185                false,
186                None,
187            )
188            .await?;
189        }
190
191        Ok(())
192    }
193
194    async fn handle_forwarded(
195        &self,
196        message: &crate::models::Message,
197        api: &VkApi,
198    ) -> VkResult<()> {
199        let count = message.fwd_messages.len();
200
201        api.messages_send(
202            message.peer_id,
203            &format!("Forwarded messages: {}", count),
204            None,
205            None,
206            None,
207            Some(message.id),
208            None,
209            false,
210            false,
211            None,
212        )
213        .await?;
214
215        Ok(())
216    }
217
218    async fn handle_edit(&self, message: &crate::models::Message, api: &VkApi) -> VkResult<()> {
219        api.messages_send(
220            message.peer_id,
221            "Message was edited",
222            None,
223            None,
224            None,
225            None,
226            None,
227            true,
228            false,
229            None,
230        )
231        .await?;
232
233        Ok(())
234    }
235
236    async fn handle_message_event(
237        &self,
238        event: &crate::models::MessageEvent,
239        api: &VkApi,
240    ) -> VkResult<()> {
241        let _ = api
242            .messages_send_message_event_answer(
243                &event.event_id,
244                event.user_id,
245                event.peer_id,
246                Some(r#"{"type": "show_snackbar", "text": "Done!"}"#),
247            )
248            .await;
249
250        if let Some(payload) = &event.payload
251            && let Some(command) = payload.get("command").and_then(|c| c.as_str())
252            && command == "help"
253        {
254            api.messages_send(
255                event.peer_id,
256                "Help: Use /commands for available commands",
257                None,
258                None,
259                None,
260                None,
261                None,
262                false,
263                false,
264                None,
265            )
266            .await?;
267        }
268
269        Ok(())
270    }
271
272    async fn handle_typing(
273        &self,
274        _state: &crate::models::MessageTypingState,
275        _api: &VkApi,
276    ) -> VkResult<()> {
277        Ok(())
278    }
279
280    async fn handle_message_allow(
281        &self,
282        allow: &crate::models::MessageAllow,
283        api: &VkApi,
284    ) -> VkResult<()> {
285        api.messages_send(
286            allow.user_id,
287            "Thanks for allowing messages!",
288            None,
289            None,
290            None,
291            None,
292            None,
293            false,
294            false,
295            None,
296        )
297        .await?;
298
299        Ok(())
300    }
301}
302
303#[derive(Debug, Clone)]
304pub struct AdminHandler {
305    admin_ids: Vec<i64>,
306}
307
308impl AdminHandler {
309    pub fn new(admin_ids: Vec<i64>) -> Self {
310        Self { admin_ids }
311    }
312
313    pub fn is_admin(&self, user_id: i64) -> bool {
314        self.admin_ids.contains(&user_id)
315    }
316}
317
318#[async_trait]
319impl MessageHandler for AdminHandler {
320    async fn handle(&self, event: &Event, api: &VkApi) -> VkResult<()> {
321        if let Event::MessageNew(message) = event
322            && self.is_admin(message.from_id)
323        {
324            self.handle_admin_command(message, api).await?;
325        }
326
327        Ok(())
328    }
329}
330
331impl AdminHandler {
332    async fn handle_admin_command(
333        &self,
334        message: &crate::models::Message,
335        api: &VkApi,
336    ) -> VkResult<()> {
337        if message.text.starts_with("/admin") {
338            let keyboard = Keyboard::create_admin_menu();
339
340            api.messages_send(
341                message.peer_id,
342                "šŸ”§ Admin panel:",
343                Some(&keyboard),
344                None,
345                None,
346                None,
347                None,
348                false,
349                false,
350                None,
351            )
352            .await?;
353        }
354
355        Ok(())
356    }
357}