Skip to main content

rust_tg_bot_ext/handlers/
chat_boost.rs

1//! [`ChatBoostHandler`] -- handles chat boost updates.
2//!
3//! Ported from `python-telegram-bot`'s `ChatBoostHandler`. Supports filtering
4//! by boost type (`chat_boost`, `removed_chat_boost`, or both) and by
5//! chat ID / username.
6
7use std::collections::HashSet;
8use std::future::Future;
9use std::pin::Pin;
10use std::sync::Arc;
11
12use rust_tg_bot_raw::types::update::Update;
13
14use super::base::{Handler, HandlerCallback, HandlerResult, MatchResult};
15
16/// Which kind of chat boost update to handle.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[non_exhaustive]
19pub enum ChatBoostType {
20    /// Only `chat_boost`.
21    ChatBoost,
22    /// Only `removed_chat_boost`.
23    RemovedChatBoost,
24    /// Both `chat_boost` and `removed_chat_boost`.
25    Any,
26}
27
28/// Handler for `Update.chat_boost` and `Update.removed_chat_boost`.
29pub struct ChatBoostHandler {
30    callback: HandlerCallback,
31    boost_type: ChatBoostType,
32    chat_ids: HashSet<i64>,
33    chat_usernames: HashSet<String>,
34    block: bool,
35}
36
37impl ChatBoostHandler {
38    /// Create a new `ChatBoostHandler`.
39    pub fn new(
40        callback: HandlerCallback,
41        boost_type: ChatBoostType,
42        chat_ids: HashSet<i64>,
43        chat_usernames: HashSet<String>,
44        block: bool,
45    ) -> Self {
46        let chat_usernames = chat_usernames
47            .into_iter()
48            .map(|u| u.trim_start_matches('@').to_lowercase())
49            .collect();
50        Self {
51            callback,
52            boost_type,
53            chat_ids,
54            chat_usernames,
55            block,
56        }
57    }
58}
59
60impl Handler for ChatBoostHandler {
61    fn check_update(&self, update: &Update) -> Option<MatchResult> {
62        let has_boost = update.chat_boost().is_some();
63        let has_removed = update.removed_chat_boost().is_some();
64
65        if !has_boost && !has_removed {
66            return None;
67        }
68
69        match self.boost_type {
70            ChatBoostType::ChatBoost => {
71                if !has_boost {
72                    return None;
73                }
74            }
75            ChatBoostType::RemovedChatBoost => {
76                if !has_removed {
77                    return None;
78                }
79            }
80            ChatBoostType::Any => {}
81        }
82
83        // If no ID/username filters, accept.
84        if self.chat_ids.is_empty() && self.chat_usernames.is_empty() {
85            return Some(MatchResult::Empty);
86        }
87
88        // Extract chat info from the effective chat.
89        if let Some(chat) = update.effective_chat() {
90            if self.chat_ids.contains(&chat.id) {
91                return Some(MatchResult::Empty);
92            }
93            if let Some(ref uname) = chat.username {
94                if self.chat_usernames.contains(&uname.to_lowercase()) {
95                    return Some(MatchResult::Empty);
96                }
97            }
98        }
99
100        None
101    }
102
103    fn handle_update(
104        &self,
105        update: Arc<Update>,
106        match_result: MatchResult,
107    ) -> Pin<Box<dyn Future<Output = HandlerResult> + Send>> {
108        (self.callback)(update, match_result)
109    }
110
111    fn block(&self) -> bool {
112        self.block
113    }
114}