Skip to main content

Context

Struct Context 

Source
pub struct Context {
    pub update: Update,
    /* private fields */
}
Expand description

Контекст, передаваемый каждому обработчику.

Содержит ссылку на клиент бота и обновление, которое вызвало этот обработчик.

Fields§

§update: Update

Implementations§

Source§

impl Context

Source

pub fn new(bot: Arc<MaxClient>, update: Update) -> Self

Source

pub fn bot(&self) -> &MaxClient

Возвращает ссылку на клиент бота.

Examples found in repository?
examples/fruits-bot.rs (line 138)
130async fn main() -> Result<(), Box<dyn std::error::Error>> {
131    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
132    let mut dp = Dispatcher::new(bot);
133
134    // Обработчик /start и добавления в чат (BotStarted)
135    dp.on_command("/start", |ctx| async move {
136        let chat_id = ctx.chat_id().unwrap();
137        let params = greeting()?.chat_id(chat_id).build();
138        ctx.bot().send_message(params).await?;
139        Ok(())
140    });
141
142    // Обработчик добавления бота в чат
143    dp.on_bot_started(|ctx| async move {
144        let chat_id = ctx.chat_id().unwrap();
145        let params = greeting()?.chat_id(chat_id).build();
146        ctx.bot().send_message(params).await?;
147        Ok(())
148    });
149
150    // Обработчик callback-кнопок
151    dp.on_callback(|ctx| async move {
152        // Извлекаем payload
153        let callback = match &ctx.update {
154            maxbot::Update::MessageCallback { callback, .. } => callback,
155            _ => return Ok(()),
156        };
157        let payload = &callback.payload;
158
159        // Формируем JSON для ответа в зависимости от payload
160        let message_json = match payload.as_str() {
161            "stand" => Some(builder_to_json(ctx.bot(), fruit_menu()?).await?),
162            "fruit:apple" => Some(builder_to_json(ctx.bot(), apple_message()?).await?),
163            "fruit:pear" => Some(builder_to_json(ctx.bot(), pear_message()?).await?),
164            "fruit:orange" => Some(builder_to_json(ctx.bot(), orange_message()?).await?),
165            "fruit:tomato" => Some(builder_to_json(ctx.bot(), tomato_message()?).await?),
166            _ => None,
167        };
168
169        if let Some(json) = message_json {
170            // Отвечаем на callback, заменяя сообщение
171            ctx.answer_callback_raw(None, Some(json)).await?;
172        } else {
173            // Неизвестный payload – просто подтверждаем без изменений
174            ctx.answer_callback_raw(None, None).await?;
175        }
176        Ok(())
177    });
178
179    println!("🍎🍐🍊🍅 Фруктовый бот запущен! Начните общение с ботом или выполните /start в существующем диалоге. Нажмите Ctrl+C для остановки.");
180    dp.start_polling().await;
181    Ok(())
182}
More examples
Hide additional examples
examples/echo-bot.rs (line 90)
14async fn main() -> Result<(), Box<dyn std::error::Error>> {
15    // Инициализируем клиент из переменной окружения MAXBOT_TOKEN
16    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
17
18    // Создаём диспетчер
19    let mut dp = Dispatcher::new(bot);
20
21    // Обработчик команды /start
22    dp.on_command("/start", |ctx| async move {
23        ctx.reply_markdown("Привет! Я эхо-бот.\nОтправь мне любое сообщение, и я отвечу тем же.").await?;
24        Ok(())
25    });
26
27    // Обработчик любого сообщения (эхо)
28    dp.on_message(|ctx| async move {
29        let chat_id = ctx.chat_id().unwrap();
30
31        // 1. Текст
32        let text = ctx.text().unwrap_or("").to_string();
33
34        // 2. Вложения
35        let mut attachments = Vec::new();
36        if let Some(msg) = ctx.message() {
37            if let Some(body) = &msg.body {
38                // Обработка вложений
39                for att in &body.attachments {
40                    match att {
41                        AttachmentData::Image { payload } => {
42                            if let Some(token) = &payload.token {
43                                attachments.push(Attachment::image_token(token));
44                            }
45                        }
46                        AttachmentData::Video { payload } => {
47                            if let Some(token) = &payload.token {
48                                attachments.push(Attachment::video_token(token));
49                            }
50                        }
51                        AttachmentData::Audio { payload } => {
52                            if let Some(token) = &payload.token {
53                                attachments.push(Attachment::audio_token(token));
54                            }
55                        }
56                        AttachmentData::File { payload } => {
57                            if let Some(token) = &payload.token {
58                                attachments.push(Attachment::file_token(token));
59                            }
60                        }
61                        AttachmentData::Sticker { payload } => {
62                            attachments.push(Attachment::sticker(&payload.code));
63                        }
64                        AttachmentData::InlineKeyboard { .. } => {
65                            unimplemented!("Ignore keyboard");
66                        }
67                        AttachmentData::Location { payload } => {
68                            attachments.push(Attachment::location(payload.latitude, payload.longitude));
69                        }
70                        AttachmentData::Contact { payload } => {
71                            let contact_data = ContactData {
72                                name: payload.name.clone(),
73                                contact_id: payload.contact_id,
74                                vcf_info: payload.vcf_info.clone(),
75                                vcf_phone: payload.vcf_phone.clone(),
76                            };
77                            attachments.push(Attachment::contact(contact_data));
78                        }
79                        _ => {}
80                    }
81                }
82            }
83        }
84
85        let params = SendMessageParamsBuilder::new()
86            .text(&text)
87            .chat_id(chat_id)
88            .attachments(attachments)
89            .build();
90        ctx.bot().send_message(params).await?;
91        Ok(())
92    });
93
94    println!("Бот запущен. Нажмите Ctrl+C для остановки.");
95    dp.start_polling().await;
96
97    Ok(())
98}
examples/video-info-bot.rs (line 60)
17async fn main() -> Result<(), Box<dyn std::error::Error>> {
18    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
19    let mut dp = Dispatcher::new(bot);
20
21    dp.on_command("/start", |ctx| async move {
22        ctx.reply_markdown("Отправьте мне любое видео, и я расскажу о нём всё, что знаю! 🎬").await?;
23        Ok(())
24    });
25
26    dp.on_message(|ctx| async move {
27        let chat_id = ctx.chat_id().unwrap();
28
29        // Ищем видео вложение
30        let video_token = {
31            let msg = match ctx.message() {
32                Some(m) => m,
33                None => return Ok(()),
34            };
35            let body = match &msg.body {
36                Some(b) => b,
37                None => return Ok(()),
38            };
39            let mut token = None;
40            for att in &body.attachments {
41                if let maxbot::AttachmentData::Video { payload } = att {
42                    if let Some(tok) = &payload.token {
43                        token = Some(tok.clone());
44                        break;
45                    }
46                }
47            }
48            token
49        };
50
51        let video_token = match video_token {
52            Some(tok) => tok,
53            None => {
54                ctx.reply_text("Это не видео (не удалось найти видео-вложение).").await?;
55                return Ok(());
56            }
57        };
58
59        // Получаем информацию с таймаутом
60        let info = match timeout(Duration::from_secs(5), ctx.bot().get_video_info(&video_token)).await {
61            Ok(Ok(i)) => i,
62            Ok(Err(e)) => {
63                ctx.reply_text(&format!("Ошибка получения информации: {}", e)).await?;
64                return Ok(());
65            }
66            Err(_) => {
67                ctx.reply_text("Сервер MAX не ответил вовремя.").await?;
68                return Ok(());
69            }
70        };
71
72        // Длительность приходит в миллисекундах
73        let duration_secs = info.duration / 1000;
74        let duration = Duration::from_secs(duration_secs as u64);
75        let minutes = duration.as_secs() / 60;
76        let seconds = duration.as_secs() % 60;
77        let duration_str = if minutes > 0 {
78            format!("{} мин {} сек", minutes, seconds)
79        } else {
80            format!("{} сек", seconds)
81        };
82
83        // Собираем доступные качества (без ссылок)
84        let mut available_qualities = Vec::new();
85        let quality_map = [
86            ("mp4_1080", "1080p"),
87            ("mp4_720", "720p"),
88            ("mp4_480", "480p"),
89            ("mp4_360", "360p"),
90            ("mp4_240", "240p"),
91            ("mp4_144", "144p"),
92            ("hls", "HLS"),
93        ];
94        for (key, name) in quality_map.iter() {
95            if info.urls.contains_key(*key) {
96                available_qualities.push(*name);
97            }
98        }
99        let qualities_text = if available_qualities.is_empty() {
100            "неизвестно".to_string()
101        } else {
102            available_qualities.join(", ")
103        };
104
105        let text = format!(
106            "🎥 **Информация о видео**\n\n\
107             📐 Разрешение: {}×{}\n\
108             ⏱ Длительность: {}\n\
109             🎞 Качество: {}",
110            info.width, info.height,
111            duration_str,
112            qualities_text
113        );
114
115        let builder = SendMessageParamsBuilder::new()
116            .text(&text)
117            .chat_id(chat_id)
118            .format_markdown();
119        if let Err(e) = ctx.bot().send_message_builder(builder).await {
120            eprintln!("Ошибка отправки сообщения: {}", e);
121        }
122        Ok(())
123    });
124
125    println!("🎬 Видео-бот запущен. Отправьте видео, чтобы получить информацию.");
126    dp.start_polling().await;
127    Ok(())
128}
examples/ask-phone-bot.rs (line 40)
9async fn main() -> Result<(), Box<dyn std::error::Error>> {
10    let token = env::var("MAXBOT_TOKEN").expect("MAXBOT_TOKEN not set");
11    let real_token = env::var("MAXBOT_REAL_TOKEN").unwrap_or_else(|_| token.clone());
12
13    let mut check_hash = true;
14    let mut client = MaxClient::new(token);
15    if let Ok(proxy_url) = env::var("MAXBOT_PROXY") {
16        client.set_base_url(proxy_url);
17        if real_token.starts_with("Basic ") {
18            check_hash = false;
19        }
20    }
21
22    if !check_hash {
23        println!("Токен бота подменён. Требуется указать MAXBOT_REAL_TOKEN для проверки номера телефона.");
24    }
25
26    let bot = client;
27    let mut dp = Dispatcher::new(bot);
28
29    // Команда /start – отправляем кнопку запроса контакта
30    dp.on_command("/start", |ctx| async move {
31        let chat_id = ctx.chat_id().unwrap();
32        let keyboard = InlineKeyboardBuilder::new()
33            .button(InlineKeyboardButton::request_contact("Передать 🔀"))
34            .build()?;
35        let body = SendMessageParamsBuilder::new()
36            .text("Предоставьте свои данные")
37            .chat_id(chat_id)
38            .attachment(Attachment::inline_keyboard(keyboard))
39            .build();
40        ctx.bot().send_message(body).await?;
41        Ok(())
42    });
43
44    // Подготовка токена проверки для передачи в замыкание
45    let check_bytes = real_token.into_bytes();
46
47    // Обработка входящего контакта
48    dp.on_message(move |ctx| {
49        let token_bytes = check_bytes.clone();
50        async move {
51            let msg = match ctx.message() {
52                Some(m) => m,
53                None => return Ok(()),
54            };
55            let attachments = match &msg.body {
56                Some(b) => &b.attachments,
57                None => return Ok(()),
58            };
59            for att in attachments {
60                if let maxbot::AttachmentData::Contact { payload } = att {
61                    let mut response = String::from("Вы предоставили следующую информацию:\n");
62                    let mut has_phone = false;
63
64                    // Разбор vCard, если есть
65                    if let Some(vcf) = &payload.vcf_info {
66                        match parse_vcard(vcf) {
67                            Ok(card) => {
68                                if let Some(fn_) = card.get_formatted_name() {
69                                    response.push_str(&format!("Имя: {}\n", fn_));
70                                }
71                                for phone in card.get_phone_numbers() {
72                                    response.push_str(&format!("Телефон: {}\n", phone));
73                                    has_phone = !phone.is_empty();
74                                }
75                                for email in card.get_emails() {
76                                    response.push_str(&format!("Email: {}\n", email));
77                                }
78                            }
79                            Err(e) => response.push_str(&format!("Ошибка чтения vCard: {}\n", e)),
80                        }
81                    } else {
82                        if let Some(name) = &payload.name {
83                            response.push_str(&format!("Имя: {}\n", name));
84                        }
85                        if let Some(phone) = &payload.vcf_phone {
86                            response.push_str(&format!("Телефон: {}\n", phone));
87                            has_phone = !phone.is_empty();
88                        }
89                    }
90
91                    // Проверка контрольной суммы
92                    if check_hash && has_phone {
93                        if payload.verify_hash(&token_bytes) {
94                            response.push_str("✅ Номер телефона подтверждён.\n");
95                        } else {
96                            response.push_str("❌ Номер телефона не подтверждён.\n");
97                        }
98                    }
99
100                    ctx.reply_text(&response).await?;
101                    return Ok(());
102                }
103            }
104            Ok(())
105        }
106    });
107
108    println!("Бот запущен. Отправьте команду /start для запроса контактных данных.");
109    dp.start_polling().await;
110    Ok(())
111}
Source

pub fn message(&self) -> Option<&Message>

Возвращает сообщение, если обновление является созданным или отредактированным сообщением.

Examples found in repository?
examples/echo-bot.rs (line 36)
14async fn main() -> Result<(), Box<dyn std::error::Error>> {
15    // Инициализируем клиент из переменной окружения MAXBOT_TOKEN
16    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
17
18    // Создаём диспетчер
19    let mut dp = Dispatcher::new(bot);
20
21    // Обработчик команды /start
22    dp.on_command("/start", |ctx| async move {
23        ctx.reply_markdown("Привет! Я эхо-бот.\nОтправь мне любое сообщение, и я отвечу тем же.").await?;
24        Ok(())
25    });
26
27    // Обработчик любого сообщения (эхо)
28    dp.on_message(|ctx| async move {
29        let chat_id = ctx.chat_id().unwrap();
30
31        // 1. Текст
32        let text = ctx.text().unwrap_or("").to_string();
33
34        // 2. Вложения
35        let mut attachments = Vec::new();
36        if let Some(msg) = ctx.message() {
37            if let Some(body) = &msg.body {
38                // Обработка вложений
39                for att in &body.attachments {
40                    match att {
41                        AttachmentData::Image { payload } => {
42                            if let Some(token) = &payload.token {
43                                attachments.push(Attachment::image_token(token));
44                            }
45                        }
46                        AttachmentData::Video { payload } => {
47                            if let Some(token) = &payload.token {
48                                attachments.push(Attachment::video_token(token));
49                            }
50                        }
51                        AttachmentData::Audio { payload } => {
52                            if let Some(token) = &payload.token {
53                                attachments.push(Attachment::audio_token(token));
54                            }
55                        }
56                        AttachmentData::File { payload } => {
57                            if let Some(token) = &payload.token {
58                                attachments.push(Attachment::file_token(token));
59                            }
60                        }
61                        AttachmentData::Sticker { payload } => {
62                            attachments.push(Attachment::sticker(&payload.code));
63                        }
64                        AttachmentData::InlineKeyboard { .. } => {
65                            unimplemented!("Ignore keyboard");
66                        }
67                        AttachmentData::Location { payload } => {
68                            attachments.push(Attachment::location(payload.latitude, payload.longitude));
69                        }
70                        AttachmentData::Contact { payload } => {
71                            let contact_data = ContactData {
72                                name: payload.name.clone(),
73                                contact_id: payload.contact_id,
74                                vcf_info: payload.vcf_info.clone(),
75                                vcf_phone: payload.vcf_phone.clone(),
76                            };
77                            attachments.push(Attachment::contact(contact_data));
78                        }
79                        _ => {}
80                    }
81                }
82            }
83        }
84
85        let params = SendMessageParamsBuilder::new()
86            .text(&text)
87            .chat_id(chat_id)
88            .attachments(attachments)
89            .build();
90        ctx.bot().send_message(params).await?;
91        Ok(())
92    });
93
94    println!("Бот запущен. Нажмите Ctrl+C для остановки.");
95    dp.start_polling().await;
96
97    Ok(())
98}
More examples
Hide additional examples
examples/video-info-bot.rs (line 31)
17async fn main() -> Result<(), Box<dyn std::error::Error>> {
18    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
19    let mut dp = Dispatcher::new(bot);
20
21    dp.on_command("/start", |ctx| async move {
22        ctx.reply_markdown("Отправьте мне любое видео, и я расскажу о нём всё, что знаю! 🎬").await?;
23        Ok(())
24    });
25
26    dp.on_message(|ctx| async move {
27        let chat_id = ctx.chat_id().unwrap();
28
29        // Ищем видео вложение
30        let video_token = {
31            let msg = match ctx.message() {
32                Some(m) => m,
33                None => return Ok(()),
34            };
35            let body = match &msg.body {
36                Some(b) => b,
37                None => return Ok(()),
38            };
39            let mut token = None;
40            for att in &body.attachments {
41                if let maxbot::AttachmentData::Video { payload } = att {
42                    if let Some(tok) = &payload.token {
43                        token = Some(tok.clone());
44                        break;
45                    }
46                }
47            }
48            token
49        };
50
51        let video_token = match video_token {
52            Some(tok) => tok,
53            None => {
54                ctx.reply_text("Это не видео (не удалось найти видео-вложение).").await?;
55                return Ok(());
56            }
57        };
58
59        // Получаем информацию с таймаутом
60        let info = match timeout(Duration::from_secs(5), ctx.bot().get_video_info(&video_token)).await {
61            Ok(Ok(i)) => i,
62            Ok(Err(e)) => {
63                ctx.reply_text(&format!("Ошибка получения информации: {}", e)).await?;
64                return Ok(());
65            }
66            Err(_) => {
67                ctx.reply_text("Сервер MAX не ответил вовремя.").await?;
68                return Ok(());
69            }
70        };
71
72        // Длительность приходит в миллисекундах
73        let duration_secs = info.duration / 1000;
74        let duration = Duration::from_secs(duration_secs as u64);
75        let minutes = duration.as_secs() / 60;
76        let seconds = duration.as_secs() % 60;
77        let duration_str = if minutes > 0 {
78            format!("{} мин {} сек", minutes, seconds)
79        } else {
80            format!("{} сек", seconds)
81        };
82
83        // Собираем доступные качества (без ссылок)
84        let mut available_qualities = Vec::new();
85        let quality_map = [
86            ("mp4_1080", "1080p"),
87            ("mp4_720", "720p"),
88            ("mp4_480", "480p"),
89            ("mp4_360", "360p"),
90            ("mp4_240", "240p"),
91            ("mp4_144", "144p"),
92            ("hls", "HLS"),
93        ];
94        for (key, name) in quality_map.iter() {
95            if info.urls.contains_key(*key) {
96                available_qualities.push(*name);
97            }
98        }
99        let qualities_text = if available_qualities.is_empty() {
100            "неизвестно".to_string()
101        } else {
102            available_qualities.join(", ")
103        };
104
105        let text = format!(
106            "🎥 **Информация о видео**\n\n\
107             📐 Разрешение: {}×{}\n\
108             ⏱ Длительность: {}\n\
109             🎞 Качество: {}",
110            info.width, info.height,
111            duration_str,
112            qualities_text
113        );
114
115        let builder = SendMessageParamsBuilder::new()
116            .text(&text)
117            .chat_id(chat_id)
118            .format_markdown();
119        if let Err(e) = ctx.bot().send_message_builder(builder).await {
120            eprintln!("Ошибка отправки сообщения: {}", e);
121        }
122        Ok(())
123    });
124
125    println!("🎬 Видео-бот запущен. Отправьте видео, чтобы получить информацию.");
126    dp.start_polling().await;
127    Ok(())
128}
examples/ask-phone-bot.rs (line 51)
9async fn main() -> Result<(), Box<dyn std::error::Error>> {
10    let token = env::var("MAXBOT_TOKEN").expect("MAXBOT_TOKEN not set");
11    let real_token = env::var("MAXBOT_REAL_TOKEN").unwrap_or_else(|_| token.clone());
12
13    let mut check_hash = true;
14    let mut client = MaxClient::new(token);
15    if let Ok(proxy_url) = env::var("MAXBOT_PROXY") {
16        client.set_base_url(proxy_url);
17        if real_token.starts_with("Basic ") {
18            check_hash = false;
19        }
20    }
21
22    if !check_hash {
23        println!("Токен бота подменён. Требуется указать MAXBOT_REAL_TOKEN для проверки номера телефона.");
24    }
25
26    let bot = client;
27    let mut dp = Dispatcher::new(bot);
28
29    // Команда /start – отправляем кнопку запроса контакта
30    dp.on_command("/start", |ctx| async move {
31        let chat_id = ctx.chat_id().unwrap();
32        let keyboard = InlineKeyboardBuilder::new()
33            .button(InlineKeyboardButton::request_contact("Передать 🔀"))
34            .build()?;
35        let body = SendMessageParamsBuilder::new()
36            .text("Предоставьте свои данные")
37            .chat_id(chat_id)
38            .attachment(Attachment::inline_keyboard(keyboard))
39            .build();
40        ctx.bot().send_message(body).await?;
41        Ok(())
42    });
43
44    // Подготовка токена проверки для передачи в замыкание
45    let check_bytes = real_token.into_bytes();
46
47    // Обработка входящего контакта
48    dp.on_message(move |ctx| {
49        let token_bytes = check_bytes.clone();
50        async move {
51            let msg = match ctx.message() {
52                Some(m) => m,
53                None => return Ok(()),
54            };
55            let attachments = match &msg.body {
56                Some(b) => &b.attachments,
57                None => return Ok(()),
58            };
59            for att in attachments {
60                if let maxbot::AttachmentData::Contact { payload } = att {
61                    let mut response = String::from("Вы предоставили следующую информацию:\n");
62                    let mut has_phone = false;
63
64                    // Разбор vCard, если есть
65                    if let Some(vcf) = &payload.vcf_info {
66                        match parse_vcard(vcf) {
67                            Ok(card) => {
68                                if let Some(fn_) = card.get_formatted_name() {
69                                    response.push_str(&format!("Имя: {}\n", fn_));
70                                }
71                                for phone in card.get_phone_numbers() {
72                                    response.push_str(&format!("Телефон: {}\n", phone));
73                                    has_phone = !phone.is_empty();
74                                }
75                                for email in card.get_emails() {
76                                    response.push_str(&format!("Email: {}\n", email));
77                                }
78                            }
79                            Err(e) => response.push_str(&format!("Ошибка чтения vCard: {}\n", e)),
80                        }
81                    } else {
82                        if let Some(name) = &payload.name {
83                            response.push_str(&format!("Имя: {}\n", name));
84                        }
85                        if let Some(phone) = &payload.vcf_phone {
86                            response.push_str(&format!("Телефон: {}\n", phone));
87                            has_phone = !phone.is_empty();
88                        }
89                    }
90
91                    // Проверка контрольной суммы
92                    if check_hash && has_phone {
93                        if payload.verify_hash(&token_bytes) {
94                            response.push_str("✅ Номер телефона подтверждён.\n");
95                        } else {
96                            response.push_str("❌ Номер телефона не подтверждён.\n");
97                        }
98                    }
99
100                    ctx.reply_text(&response).await?;
101                    return Ok(());
102                }
103            }
104            Ok(())
105        }
106    });
107
108    println!("Бот запущен. Отправьте команду /start для запроса контактных данных.");
109    dp.start_polling().await;
110    Ok(())
111}
Source

pub fn chat_id(&self) -> Option<i64>

Возвращает идентификатор чата, если обновление связано с чатом.

Examples found in repository?
examples/fruits-bot.rs (line 136)
130async fn main() -> Result<(), Box<dyn std::error::Error>> {
131    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
132    let mut dp = Dispatcher::new(bot);
133
134    // Обработчик /start и добавления в чат (BotStarted)
135    dp.on_command("/start", |ctx| async move {
136        let chat_id = ctx.chat_id().unwrap();
137        let params = greeting()?.chat_id(chat_id).build();
138        ctx.bot().send_message(params).await?;
139        Ok(())
140    });
141
142    // Обработчик добавления бота в чат
143    dp.on_bot_started(|ctx| async move {
144        let chat_id = ctx.chat_id().unwrap();
145        let params = greeting()?.chat_id(chat_id).build();
146        ctx.bot().send_message(params).await?;
147        Ok(())
148    });
149
150    // Обработчик callback-кнопок
151    dp.on_callback(|ctx| async move {
152        // Извлекаем payload
153        let callback = match &ctx.update {
154            maxbot::Update::MessageCallback { callback, .. } => callback,
155            _ => return Ok(()),
156        };
157        let payload = &callback.payload;
158
159        // Формируем JSON для ответа в зависимости от payload
160        let message_json = match payload.as_str() {
161            "stand" => Some(builder_to_json(ctx.bot(), fruit_menu()?).await?),
162            "fruit:apple" => Some(builder_to_json(ctx.bot(), apple_message()?).await?),
163            "fruit:pear" => Some(builder_to_json(ctx.bot(), pear_message()?).await?),
164            "fruit:orange" => Some(builder_to_json(ctx.bot(), orange_message()?).await?),
165            "fruit:tomato" => Some(builder_to_json(ctx.bot(), tomato_message()?).await?),
166            _ => None,
167        };
168
169        if let Some(json) = message_json {
170            // Отвечаем на callback, заменяя сообщение
171            ctx.answer_callback_raw(None, Some(json)).await?;
172        } else {
173            // Неизвестный payload – просто подтверждаем без изменений
174            ctx.answer_callback_raw(None, None).await?;
175        }
176        Ok(())
177    });
178
179    println!("🍎🍐🍊🍅 Фруктовый бот запущен! Начните общение с ботом или выполните /start в существующем диалоге. Нажмите Ctrl+C для остановки.");
180    dp.start_polling().await;
181    Ok(())
182}
More examples
Hide additional examples
examples/echo-bot.rs (line 29)
14async fn main() -> Result<(), Box<dyn std::error::Error>> {
15    // Инициализируем клиент из переменной окружения MAXBOT_TOKEN
16    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
17
18    // Создаём диспетчер
19    let mut dp = Dispatcher::new(bot);
20
21    // Обработчик команды /start
22    dp.on_command("/start", |ctx| async move {
23        ctx.reply_markdown("Привет! Я эхо-бот.\nОтправь мне любое сообщение, и я отвечу тем же.").await?;
24        Ok(())
25    });
26
27    // Обработчик любого сообщения (эхо)
28    dp.on_message(|ctx| async move {
29        let chat_id = ctx.chat_id().unwrap();
30
31        // 1. Текст
32        let text = ctx.text().unwrap_or("").to_string();
33
34        // 2. Вложения
35        let mut attachments = Vec::new();
36        if let Some(msg) = ctx.message() {
37            if let Some(body) = &msg.body {
38                // Обработка вложений
39                for att in &body.attachments {
40                    match att {
41                        AttachmentData::Image { payload } => {
42                            if let Some(token) = &payload.token {
43                                attachments.push(Attachment::image_token(token));
44                            }
45                        }
46                        AttachmentData::Video { payload } => {
47                            if let Some(token) = &payload.token {
48                                attachments.push(Attachment::video_token(token));
49                            }
50                        }
51                        AttachmentData::Audio { payload } => {
52                            if let Some(token) = &payload.token {
53                                attachments.push(Attachment::audio_token(token));
54                            }
55                        }
56                        AttachmentData::File { payload } => {
57                            if let Some(token) = &payload.token {
58                                attachments.push(Attachment::file_token(token));
59                            }
60                        }
61                        AttachmentData::Sticker { payload } => {
62                            attachments.push(Attachment::sticker(&payload.code));
63                        }
64                        AttachmentData::InlineKeyboard { .. } => {
65                            unimplemented!("Ignore keyboard");
66                        }
67                        AttachmentData::Location { payload } => {
68                            attachments.push(Attachment::location(payload.latitude, payload.longitude));
69                        }
70                        AttachmentData::Contact { payload } => {
71                            let contact_data = ContactData {
72                                name: payload.name.clone(),
73                                contact_id: payload.contact_id,
74                                vcf_info: payload.vcf_info.clone(),
75                                vcf_phone: payload.vcf_phone.clone(),
76                            };
77                            attachments.push(Attachment::contact(contact_data));
78                        }
79                        _ => {}
80                    }
81                }
82            }
83        }
84
85        let params = SendMessageParamsBuilder::new()
86            .text(&text)
87            .chat_id(chat_id)
88            .attachments(attachments)
89            .build();
90        ctx.bot().send_message(params).await?;
91        Ok(())
92    });
93
94    println!("Бот запущен. Нажмите Ctrl+C для остановки.");
95    dp.start_polling().await;
96
97    Ok(())
98}
examples/video-info-bot.rs (line 27)
17async fn main() -> Result<(), Box<dyn std::error::Error>> {
18    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
19    let mut dp = Dispatcher::new(bot);
20
21    dp.on_command("/start", |ctx| async move {
22        ctx.reply_markdown("Отправьте мне любое видео, и я расскажу о нём всё, что знаю! 🎬").await?;
23        Ok(())
24    });
25
26    dp.on_message(|ctx| async move {
27        let chat_id = ctx.chat_id().unwrap();
28
29        // Ищем видео вложение
30        let video_token = {
31            let msg = match ctx.message() {
32                Some(m) => m,
33                None => return Ok(()),
34            };
35            let body = match &msg.body {
36                Some(b) => b,
37                None => return Ok(()),
38            };
39            let mut token = None;
40            for att in &body.attachments {
41                if let maxbot::AttachmentData::Video { payload } = att {
42                    if let Some(tok) = &payload.token {
43                        token = Some(tok.clone());
44                        break;
45                    }
46                }
47            }
48            token
49        };
50
51        let video_token = match video_token {
52            Some(tok) => tok,
53            None => {
54                ctx.reply_text("Это не видео (не удалось найти видео-вложение).").await?;
55                return Ok(());
56            }
57        };
58
59        // Получаем информацию с таймаутом
60        let info = match timeout(Duration::from_secs(5), ctx.bot().get_video_info(&video_token)).await {
61            Ok(Ok(i)) => i,
62            Ok(Err(e)) => {
63                ctx.reply_text(&format!("Ошибка получения информации: {}", e)).await?;
64                return Ok(());
65            }
66            Err(_) => {
67                ctx.reply_text("Сервер MAX не ответил вовремя.").await?;
68                return Ok(());
69            }
70        };
71
72        // Длительность приходит в миллисекундах
73        let duration_secs = info.duration / 1000;
74        let duration = Duration::from_secs(duration_secs as u64);
75        let minutes = duration.as_secs() / 60;
76        let seconds = duration.as_secs() % 60;
77        let duration_str = if minutes > 0 {
78            format!("{} мин {} сек", minutes, seconds)
79        } else {
80            format!("{} сек", seconds)
81        };
82
83        // Собираем доступные качества (без ссылок)
84        let mut available_qualities = Vec::new();
85        let quality_map = [
86            ("mp4_1080", "1080p"),
87            ("mp4_720", "720p"),
88            ("mp4_480", "480p"),
89            ("mp4_360", "360p"),
90            ("mp4_240", "240p"),
91            ("mp4_144", "144p"),
92            ("hls", "HLS"),
93        ];
94        for (key, name) in quality_map.iter() {
95            if info.urls.contains_key(*key) {
96                available_qualities.push(*name);
97            }
98        }
99        let qualities_text = if available_qualities.is_empty() {
100            "неизвестно".to_string()
101        } else {
102            available_qualities.join(", ")
103        };
104
105        let text = format!(
106            "🎥 **Информация о видео**\n\n\
107             📐 Разрешение: {}×{}\n\
108             ⏱ Длительность: {}\n\
109             🎞 Качество: {}",
110            info.width, info.height,
111            duration_str,
112            qualities_text
113        );
114
115        let builder = SendMessageParamsBuilder::new()
116            .text(&text)
117            .chat_id(chat_id)
118            .format_markdown();
119        if let Err(e) = ctx.bot().send_message_builder(builder).await {
120            eprintln!("Ошибка отправки сообщения: {}", e);
121        }
122        Ok(())
123    });
124
125    println!("🎬 Видео-бот запущен. Отправьте видео, чтобы получить информацию.");
126    dp.start_polling().await;
127    Ok(())
128}
examples/ask-phone-bot.rs (line 31)
9async fn main() -> Result<(), Box<dyn std::error::Error>> {
10    let token = env::var("MAXBOT_TOKEN").expect("MAXBOT_TOKEN not set");
11    let real_token = env::var("MAXBOT_REAL_TOKEN").unwrap_or_else(|_| token.clone());
12
13    let mut check_hash = true;
14    let mut client = MaxClient::new(token);
15    if let Ok(proxy_url) = env::var("MAXBOT_PROXY") {
16        client.set_base_url(proxy_url);
17        if real_token.starts_with("Basic ") {
18            check_hash = false;
19        }
20    }
21
22    if !check_hash {
23        println!("Токен бота подменён. Требуется указать MAXBOT_REAL_TOKEN для проверки номера телефона.");
24    }
25
26    let bot = client;
27    let mut dp = Dispatcher::new(bot);
28
29    // Команда /start – отправляем кнопку запроса контакта
30    dp.on_command("/start", |ctx| async move {
31        let chat_id = ctx.chat_id().unwrap();
32        let keyboard = InlineKeyboardBuilder::new()
33            .button(InlineKeyboardButton::request_contact("Передать 🔀"))
34            .build()?;
35        let body = SendMessageParamsBuilder::new()
36            .text("Предоставьте свои данные")
37            .chat_id(chat_id)
38            .attachment(Attachment::inline_keyboard(keyboard))
39            .build();
40        ctx.bot().send_message(body).await?;
41        Ok(())
42    });
43
44    // Подготовка токена проверки для передачи в замыкание
45    let check_bytes = real_token.into_bytes();
46
47    // Обработка входящего контакта
48    dp.on_message(move |ctx| {
49        let token_bytes = check_bytes.clone();
50        async move {
51            let msg = match ctx.message() {
52                Some(m) => m,
53                None => return Ok(()),
54            };
55            let attachments = match &msg.body {
56                Some(b) => &b.attachments,
57                None => return Ok(()),
58            };
59            for att in attachments {
60                if let maxbot::AttachmentData::Contact { payload } = att {
61                    let mut response = String::from("Вы предоставили следующую информацию:\n");
62                    let mut has_phone = false;
63
64                    // Разбор vCard, если есть
65                    if let Some(vcf) = &payload.vcf_info {
66                        match parse_vcard(vcf) {
67                            Ok(card) => {
68                                if let Some(fn_) = card.get_formatted_name() {
69                                    response.push_str(&format!("Имя: {}\n", fn_));
70                                }
71                                for phone in card.get_phone_numbers() {
72                                    response.push_str(&format!("Телефон: {}\n", phone));
73                                    has_phone = !phone.is_empty();
74                                }
75                                for email in card.get_emails() {
76                                    response.push_str(&format!("Email: {}\n", email));
77                                }
78                            }
79                            Err(e) => response.push_str(&format!("Ошибка чтения vCard: {}\n", e)),
80                        }
81                    } else {
82                        if let Some(name) = &payload.name {
83                            response.push_str(&format!("Имя: {}\n", name));
84                        }
85                        if let Some(phone) = &payload.vcf_phone {
86                            response.push_str(&format!("Телефон: {}\n", phone));
87                            has_phone = !phone.is_empty();
88                        }
89                    }
90
91                    // Проверка контрольной суммы
92                    if check_hash && has_phone {
93                        if payload.verify_hash(&token_bytes) {
94                            response.push_str("✅ Номер телефона подтверждён.\n");
95                        } else {
96                            response.push_str("❌ Номер телефона не подтверждён.\n");
97                        }
98                    }
99
100                    ctx.reply_text(&response).await?;
101                    return Ok(());
102                }
103            }
104            Ok(())
105        }
106    });
107
108    println!("Бот запущен. Отправьте команду /start для запроса контактных данных.");
109    dp.start_polling().await;
110    Ok(())
111}
Source

pub fn user_id(&self) -> Option<i64>

Возвращает идентификатор пользователя — инициатора обновления.

Source

pub fn text(&self) -> Option<&str>

Возвращает текст сообщения, если это обновление является сообщением.

Examples found in repository?
examples/echo-bot.rs (line 32)
14async fn main() -> Result<(), Box<dyn std::error::Error>> {
15    // Инициализируем клиент из переменной окружения MAXBOT_TOKEN
16    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
17
18    // Создаём диспетчер
19    let mut dp = Dispatcher::new(bot);
20
21    // Обработчик команды /start
22    dp.on_command("/start", |ctx| async move {
23        ctx.reply_markdown("Привет! Я эхо-бот.\nОтправь мне любое сообщение, и я отвечу тем же.").await?;
24        Ok(())
25    });
26
27    // Обработчик любого сообщения (эхо)
28    dp.on_message(|ctx| async move {
29        let chat_id = ctx.chat_id().unwrap();
30
31        // 1. Текст
32        let text = ctx.text().unwrap_or("").to_string();
33
34        // 2. Вложения
35        let mut attachments = Vec::new();
36        if let Some(msg) = ctx.message() {
37            if let Some(body) = &msg.body {
38                // Обработка вложений
39                for att in &body.attachments {
40                    match att {
41                        AttachmentData::Image { payload } => {
42                            if let Some(token) = &payload.token {
43                                attachments.push(Attachment::image_token(token));
44                            }
45                        }
46                        AttachmentData::Video { payload } => {
47                            if let Some(token) = &payload.token {
48                                attachments.push(Attachment::video_token(token));
49                            }
50                        }
51                        AttachmentData::Audio { payload } => {
52                            if let Some(token) = &payload.token {
53                                attachments.push(Attachment::audio_token(token));
54                            }
55                        }
56                        AttachmentData::File { payload } => {
57                            if let Some(token) = &payload.token {
58                                attachments.push(Attachment::file_token(token));
59                            }
60                        }
61                        AttachmentData::Sticker { payload } => {
62                            attachments.push(Attachment::sticker(&payload.code));
63                        }
64                        AttachmentData::InlineKeyboard { .. } => {
65                            unimplemented!("Ignore keyboard");
66                        }
67                        AttachmentData::Location { payload } => {
68                            attachments.push(Attachment::location(payload.latitude, payload.longitude));
69                        }
70                        AttachmentData::Contact { payload } => {
71                            let contact_data = ContactData {
72                                name: payload.name.clone(),
73                                contact_id: payload.contact_id,
74                                vcf_info: payload.vcf_info.clone(),
75                                vcf_phone: payload.vcf_phone.clone(),
76                            };
77                            attachments.push(Attachment::contact(contact_data));
78                        }
79                        _ => {}
80                    }
81                }
82            }
83        }
84
85        let params = SendMessageParamsBuilder::new()
86            .text(&text)
87            .chat_id(chat_id)
88            .attachments(attachments)
89            .build();
90        ctx.bot().send_message(params).await?;
91        Ok(())
92    });
93
94    println!("Бот запущен. Нажмите Ctrl+C для остановки.");
95    dp.start_polling().await;
96
97    Ok(())
98}
Source

pub async fn reply_text(&self, text: &str) -> Result<Vec<String>>

Отправляет простой текстовый ответ в тот же чат.

Examples found in repository?
examples/video-info-bot.rs (line 54)
17async fn main() -> Result<(), Box<dyn std::error::Error>> {
18    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
19    let mut dp = Dispatcher::new(bot);
20
21    dp.on_command("/start", |ctx| async move {
22        ctx.reply_markdown("Отправьте мне любое видео, и я расскажу о нём всё, что знаю! 🎬").await?;
23        Ok(())
24    });
25
26    dp.on_message(|ctx| async move {
27        let chat_id = ctx.chat_id().unwrap();
28
29        // Ищем видео вложение
30        let video_token = {
31            let msg = match ctx.message() {
32                Some(m) => m,
33                None => return Ok(()),
34            };
35            let body = match &msg.body {
36                Some(b) => b,
37                None => return Ok(()),
38            };
39            let mut token = None;
40            for att in &body.attachments {
41                if let maxbot::AttachmentData::Video { payload } = att {
42                    if let Some(tok) = &payload.token {
43                        token = Some(tok.clone());
44                        break;
45                    }
46                }
47            }
48            token
49        };
50
51        let video_token = match video_token {
52            Some(tok) => tok,
53            None => {
54                ctx.reply_text("Это не видео (не удалось найти видео-вложение).").await?;
55                return Ok(());
56            }
57        };
58
59        // Получаем информацию с таймаутом
60        let info = match timeout(Duration::from_secs(5), ctx.bot().get_video_info(&video_token)).await {
61            Ok(Ok(i)) => i,
62            Ok(Err(e)) => {
63                ctx.reply_text(&format!("Ошибка получения информации: {}", e)).await?;
64                return Ok(());
65            }
66            Err(_) => {
67                ctx.reply_text("Сервер MAX не ответил вовремя.").await?;
68                return Ok(());
69            }
70        };
71
72        // Длительность приходит в миллисекундах
73        let duration_secs = info.duration / 1000;
74        let duration = Duration::from_secs(duration_secs as u64);
75        let minutes = duration.as_secs() / 60;
76        let seconds = duration.as_secs() % 60;
77        let duration_str = if minutes > 0 {
78            format!("{} мин {} сек", minutes, seconds)
79        } else {
80            format!("{} сек", seconds)
81        };
82
83        // Собираем доступные качества (без ссылок)
84        let mut available_qualities = Vec::new();
85        let quality_map = [
86            ("mp4_1080", "1080p"),
87            ("mp4_720", "720p"),
88            ("mp4_480", "480p"),
89            ("mp4_360", "360p"),
90            ("mp4_240", "240p"),
91            ("mp4_144", "144p"),
92            ("hls", "HLS"),
93        ];
94        for (key, name) in quality_map.iter() {
95            if info.urls.contains_key(*key) {
96                available_qualities.push(*name);
97            }
98        }
99        let qualities_text = if available_qualities.is_empty() {
100            "неизвестно".to_string()
101        } else {
102            available_qualities.join(", ")
103        };
104
105        let text = format!(
106            "🎥 **Информация о видео**\n\n\
107             📐 Разрешение: {}×{}\n\
108             ⏱ Длительность: {}\n\
109             🎞 Качество: {}",
110            info.width, info.height,
111            duration_str,
112            qualities_text
113        );
114
115        let builder = SendMessageParamsBuilder::new()
116            .text(&text)
117            .chat_id(chat_id)
118            .format_markdown();
119        if let Err(e) = ctx.bot().send_message_builder(builder).await {
120            eprintln!("Ошибка отправки сообщения: {}", e);
121        }
122        Ok(())
123    });
124
125    println!("🎬 Видео-бот запущен. Отправьте видео, чтобы получить информацию.");
126    dp.start_polling().await;
127    Ok(())
128}
More examples
Hide additional examples
examples/ask-phone-bot.rs (line 100)
9async fn main() -> Result<(), Box<dyn std::error::Error>> {
10    let token = env::var("MAXBOT_TOKEN").expect("MAXBOT_TOKEN not set");
11    let real_token = env::var("MAXBOT_REAL_TOKEN").unwrap_or_else(|_| token.clone());
12
13    let mut check_hash = true;
14    let mut client = MaxClient::new(token);
15    if let Ok(proxy_url) = env::var("MAXBOT_PROXY") {
16        client.set_base_url(proxy_url);
17        if real_token.starts_with("Basic ") {
18            check_hash = false;
19        }
20    }
21
22    if !check_hash {
23        println!("Токен бота подменён. Требуется указать MAXBOT_REAL_TOKEN для проверки номера телефона.");
24    }
25
26    let bot = client;
27    let mut dp = Dispatcher::new(bot);
28
29    // Команда /start – отправляем кнопку запроса контакта
30    dp.on_command("/start", |ctx| async move {
31        let chat_id = ctx.chat_id().unwrap();
32        let keyboard = InlineKeyboardBuilder::new()
33            .button(InlineKeyboardButton::request_contact("Передать 🔀"))
34            .build()?;
35        let body = SendMessageParamsBuilder::new()
36            .text("Предоставьте свои данные")
37            .chat_id(chat_id)
38            .attachment(Attachment::inline_keyboard(keyboard))
39            .build();
40        ctx.bot().send_message(body).await?;
41        Ok(())
42    });
43
44    // Подготовка токена проверки для передачи в замыкание
45    let check_bytes = real_token.into_bytes();
46
47    // Обработка входящего контакта
48    dp.on_message(move |ctx| {
49        let token_bytes = check_bytes.clone();
50        async move {
51            let msg = match ctx.message() {
52                Some(m) => m,
53                None => return Ok(()),
54            };
55            let attachments = match &msg.body {
56                Some(b) => &b.attachments,
57                None => return Ok(()),
58            };
59            for att in attachments {
60                if let maxbot::AttachmentData::Contact { payload } = att {
61                    let mut response = String::from("Вы предоставили следующую информацию:\n");
62                    let mut has_phone = false;
63
64                    // Разбор vCard, если есть
65                    if let Some(vcf) = &payload.vcf_info {
66                        match parse_vcard(vcf) {
67                            Ok(card) => {
68                                if let Some(fn_) = card.get_formatted_name() {
69                                    response.push_str(&format!("Имя: {}\n", fn_));
70                                }
71                                for phone in card.get_phone_numbers() {
72                                    response.push_str(&format!("Телефон: {}\n", phone));
73                                    has_phone = !phone.is_empty();
74                                }
75                                for email in card.get_emails() {
76                                    response.push_str(&format!("Email: {}\n", email));
77                                }
78                            }
79                            Err(e) => response.push_str(&format!("Ошибка чтения vCard: {}\n", e)),
80                        }
81                    } else {
82                        if let Some(name) = &payload.name {
83                            response.push_str(&format!("Имя: {}\n", name));
84                        }
85                        if let Some(phone) = &payload.vcf_phone {
86                            response.push_str(&format!("Телефон: {}\n", phone));
87                            has_phone = !phone.is_empty();
88                        }
89                    }
90
91                    // Проверка контрольной суммы
92                    if check_hash && has_phone {
93                        if payload.verify_hash(&token_bytes) {
94                            response.push_str("✅ Номер телефона подтверждён.\n");
95                        } else {
96                            response.push_str("❌ Номер телефона не подтверждён.\n");
97                        }
98                    }
99
100                    ctx.reply_text(&response).await?;
101                    return Ok(());
102                }
103            }
104            Ok(())
105        }
106    });
107
108    println!("Бот запущен. Отправьте команду /start для запроса контактных данных.");
109    dp.start_polling().await;
110    Ok(())
111}
Source

pub async fn reply_markdown(&self, text: &str) -> Result<Vec<String>>

Отправляет ответ в формате Markdown.

Examples found in repository?
examples/echo-bot.rs (line 23)
14async fn main() -> Result<(), Box<dyn std::error::Error>> {
15    // Инициализируем клиент из переменной окружения MAXBOT_TOKEN
16    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
17
18    // Создаём диспетчер
19    let mut dp = Dispatcher::new(bot);
20
21    // Обработчик команды /start
22    dp.on_command("/start", |ctx| async move {
23        ctx.reply_markdown("Привет! Я эхо-бот.\nОтправь мне любое сообщение, и я отвечу тем же.").await?;
24        Ok(())
25    });
26
27    // Обработчик любого сообщения (эхо)
28    dp.on_message(|ctx| async move {
29        let chat_id = ctx.chat_id().unwrap();
30
31        // 1. Текст
32        let text = ctx.text().unwrap_or("").to_string();
33
34        // 2. Вложения
35        let mut attachments = Vec::new();
36        if let Some(msg) = ctx.message() {
37            if let Some(body) = &msg.body {
38                // Обработка вложений
39                for att in &body.attachments {
40                    match att {
41                        AttachmentData::Image { payload } => {
42                            if let Some(token) = &payload.token {
43                                attachments.push(Attachment::image_token(token));
44                            }
45                        }
46                        AttachmentData::Video { payload } => {
47                            if let Some(token) = &payload.token {
48                                attachments.push(Attachment::video_token(token));
49                            }
50                        }
51                        AttachmentData::Audio { payload } => {
52                            if let Some(token) = &payload.token {
53                                attachments.push(Attachment::audio_token(token));
54                            }
55                        }
56                        AttachmentData::File { payload } => {
57                            if let Some(token) = &payload.token {
58                                attachments.push(Attachment::file_token(token));
59                            }
60                        }
61                        AttachmentData::Sticker { payload } => {
62                            attachments.push(Attachment::sticker(&payload.code));
63                        }
64                        AttachmentData::InlineKeyboard { .. } => {
65                            unimplemented!("Ignore keyboard");
66                        }
67                        AttachmentData::Location { payload } => {
68                            attachments.push(Attachment::location(payload.latitude, payload.longitude));
69                        }
70                        AttachmentData::Contact { payload } => {
71                            let contact_data = ContactData {
72                                name: payload.name.clone(),
73                                contact_id: payload.contact_id,
74                                vcf_info: payload.vcf_info.clone(),
75                                vcf_phone: payload.vcf_phone.clone(),
76                            };
77                            attachments.push(Attachment::contact(contact_data));
78                        }
79                        _ => {}
80                    }
81                }
82            }
83        }
84
85        let params = SendMessageParamsBuilder::new()
86            .text(&text)
87            .chat_id(chat_id)
88            .attachments(attachments)
89            .build();
90        ctx.bot().send_message(params).await?;
91        Ok(())
92    });
93
94    println!("Бот запущен. Нажмите Ctrl+C для остановки.");
95    dp.start_polling().await;
96
97    Ok(())
98}
More examples
Hide additional examples
examples/video-info-bot.rs (line 22)
17async fn main() -> Result<(), Box<dyn std::error::Error>> {
18    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
19    let mut dp = Dispatcher::new(bot);
20
21    dp.on_command("/start", |ctx| async move {
22        ctx.reply_markdown("Отправьте мне любое видео, и я расскажу о нём всё, что знаю! 🎬").await?;
23        Ok(())
24    });
25
26    dp.on_message(|ctx| async move {
27        let chat_id = ctx.chat_id().unwrap();
28
29        // Ищем видео вложение
30        let video_token = {
31            let msg = match ctx.message() {
32                Some(m) => m,
33                None => return Ok(()),
34            };
35            let body = match &msg.body {
36                Some(b) => b,
37                None => return Ok(()),
38            };
39            let mut token = None;
40            for att in &body.attachments {
41                if let maxbot::AttachmentData::Video { payload } = att {
42                    if let Some(tok) = &payload.token {
43                        token = Some(tok.clone());
44                        break;
45                    }
46                }
47            }
48            token
49        };
50
51        let video_token = match video_token {
52            Some(tok) => tok,
53            None => {
54                ctx.reply_text("Это не видео (не удалось найти видео-вложение).").await?;
55                return Ok(());
56            }
57        };
58
59        // Получаем информацию с таймаутом
60        let info = match timeout(Duration::from_secs(5), ctx.bot().get_video_info(&video_token)).await {
61            Ok(Ok(i)) => i,
62            Ok(Err(e)) => {
63                ctx.reply_text(&format!("Ошибка получения информации: {}", e)).await?;
64                return Ok(());
65            }
66            Err(_) => {
67                ctx.reply_text("Сервер MAX не ответил вовремя.").await?;
68                return Ok(());
69            }
70        };
71
72        // Длительность приходит в миллисекундах
73        let duration_secs = info.duration / 1000;
74        let duration = Duration::from_secs(duration_secs as u64);
75        let minutes = duration.as_secs() / 60;
76        let seconds = duration.as_secs() % 60;
77        let duration_str = if minutes > 0 {
78            format!("{} мин {} сек", minutes, seconds)
79        } else {
80            format!("{} сек", seconds)
81        };
82
83        // Собираем доступные качества (без ссылок)
84        let mut available_qualities = Vec::new();
85        let quality_map = [
86            ("mp4_1080", "1080p"),
87            ("mp4_720", "720p"),
88            ("mp4_480", "480p"),
89            ("mp4_360", "360p"),
90            ("mp4_240", "240p"),
91            ("mp4_144", "144p"),
92            ("hls", "HLS"),
93        ];
94        for (key, name) in quality_map.iter() {
95            if info.urls.contains_key(*key) {
96                available_qualities.push(*name);
97            }
98        }
99        let qualities_text = if available_qualities.is_empty() {
100            "неизвестно".to_string()
101        } else {
102            available_qualities.join(", ")
103        };
104
105        let text = format!(
106            "🎥 **Информация о видео**\n\n\
107             📐 Разрешение: {}×{}\n\
108             ⏱ Длительность: {}\n\
109             🎞 Качество: {}",
110            info.width, info.height,
111            duration_str,
112            qualities_text
113        );
114
115        let builder = SendMessageParamsBuilder::new()
116            .text(&text)
117            .chat_id(chat_id)
118            .format_markdown();
119        if let Err(e) = ctx.bot().send_message_builder(builder).await {
120            eprintln!("Ошибка отправки сообщения: {}", e);
121        }
122        Ok(())
123    });
124
125    println!("🎬 Видео-бот запущен. Отправьте видео, чтобы получить информацию.");
126    dp.start_polling().await;
127    Ok(())
128}
Source

pub async fn answer_callback( &self, notification: Option<&str>, new_message: Option<SendMessageParams>, ) -> Result<bool>

Отвечает на запрос обратного вызова необязательным уведомлением и/или заменяющим сообщением.

Работает только если обновление имеет тип MessageCallback.

Source

pub async fn answer_callback_raw( &self, notification: Option<&str>, message_json: Option<Value>, ) -> Result<bool>

Отвечает на запрос обратного вызова, передавая новое сообщение в виде сырого JSON.

Examples found in repository?
examples/fruits-bot.rs (line 171)
130async fn main() -> Result<(), Box<dyn std::error::Error>> {
131    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
132    let mut dp = Dispatcher::new(bot);
133
134    // Обработчик /start и добавления в чат (BotStarted)
135    dp.on_command("/start", |ctx| async move {
136        let chat_id = ctx.chat_id().unwrap();
137        let params = greeting()?.chat_id(chat_id).build();
138        ctx.bot().send_message(params).await?;
139        Ok(())
140    });
141
142    // Обработчик добавления бота в чат
143    dp.on_bot_started(|ctx| async move {
144        let chat_id = ctx.chat_id().unwrap();
145        let params = greeting()?.chat_id(chat_id).build();
146        ctx.bot().send_message(params).await?;
147        Ok(())
148    });
149
150    // Обработчик callback-кнопок
151    dp.on_callback(|ctx| async move {
152        // Извлекаем payload
153        let callback = match &ctx.update {
154            maxbot::Update::MessageCallback { callback, .. } => callback,
155            _ => return Ok(()),
156        };
157        let payload = &callback.payload;
158
159        // Формируем JSON для ответа в зависимости от payload
160        let message_json = match payload.as_str() {
161            "stand" => Some(builder_to_json(ctx.bot(), fruit_menu()?).await?),
162            "fruit:apple" => Some(builder_to_json(ctx.bot(), apple_message()?).await?),
163            "fruit:pear" => Some(builder_to_json(ctx.bot(), pear_message()?).await?),
164            "fruit:orange" => Some(builder_to_json(ctx.bot(), orange_message()?).await?),
165            "fruit:tomato" => Some(builder_to_json(ctx.bot(), tomato_message()?).await?),
166            _ => None,
167        };
168
169        if let Some(json) = message_json {
170            // Отвечаем на callback, заменяя сообщение
171            ctx.answer_callback_raw(None, Some(json)).await?;
172        } else {
173            // Неизвестный payload – просто подтверждаем без изменений
174            ctx.answer_callback_raw(None, None).await?;
175        }
176        Ok(())
177    });
178
179    println!("🍎🍐🍊🍅 Фруктовый бот запущен! Начните общение с ботом или выполните /start в существующем диалоге. Нажмите Ctrl+C для остановки.");
180    dp.start_polling().await;
181    Ok(())
182}
Source

pub async fn edit_text(&self, new_text: &str) -> Result<()>

Редактирует текст текущего сообщения (только если update содержит message).

Source

pub async fn edit_keyboard(&self, keyboard: InlineKeyboard) -> Result<()>

Заменяет inline-клавиатуру у текущего сообщения.

Source

pub async fn edit_message(&self, params: EditMessageParams) -> Result<()>

Общий метод редактирования сообщения.

Source

pub async fn answer_callback_notification( &self, notification: &str, ) -> Result<bool>

Отвечает на callback только всплывающим уведомлением (не меняя сообщение).

Source

pub async fn answer_callback_replace( &self, message: NewMessageBody, ) -> Result<bool>

Отвечает на callback, полностью заменяя сообщение (текст + клавиатура + вложения).

Source

pub async fn answer_callback_edit_text(&self, new_text: &str) -> Result<bool>

Отвечает на callback, изменяя только текст сообщения (клавиатура и вложения остаются).

Source

pub async fn answer_callback_edit_keyboard( &self, keyboard: InlineKeyboard, ) -> Result<bool>

Отвечает на callback, изменяя только клавиатуру.

Trait Implementations§

Source§

impl Clone for Context

Source§

fn clone(&self) -> Context

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more