pub struct Context {
pub update: Update,
/* private fields */
}Expand description
Контекст, передаваемый каждому обработчику.
Содержит ссылку на клиент бота и обновление, которое вызвало этот обработчик.
Fields§
§update: UpdateImplementations§
Source§impl Context
impl Context
pub fn new(bot: Arc<MaxClient>, update: Update) -> Self
Sourcepub fn bot(&self) -> &MaxClient
pub fn bot(&self) -> &MaxClient
Возвращает ссылку на клиент бота.
Examples found in repository?
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
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}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}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}Sourcepub fn message(&self) -> Option<&Message>
pub fn message(&self) -> Option<&Message>
Возвращает сообщение, если обновление является созданным или отредактированным сообщением.
Examples found in repository?
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
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}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}Sourcepub fn chat_id(&self) -> Option<i64>
pub fn chat_id(&self) -> Option<i64>
Возвращает идентификатор чата, если обновление связано с чатом.
Examples found in repository?
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
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}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}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}Sourcepub fn user_id(&self) -> Option<i64>
pub fn user_id(&self) -> Option<i64>
Возвращает идентификатор пользователя — инициатора обновления.
Sourcepub fn text(&self) -> Option<&str>
pub fn text(&self) -> Option<&str>
Возвращает текст сообщения, если это обновление является сообщением.
Examples found in repository?
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}Sourcepub async fn reply_text(&self, text: &str) -> Result<Vec<String>>
pub async fn reply_text(&self, text: &str) -> Result<Vec<String>>
Отправляет простой текстовый ответ в тот же чат.
Examples found in repository?
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
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}Sourcepub async fn reply_markdown(&self, text: &str) -> Result<Vec<String>>
pub async fn reply_markdown(&self, text: &str) -> Result<Vec<String>>
Отправляет ответ в формате Markdown.
Examples found in repository?
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
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}Sourcepub async fn answer_callback(
&self,
notification: Option<&str>,
new_message: Option<SendMessageParams>,
) -> Result<bool>
pub async fn answer_callback( &self, notification: Option<&str>, new_message: Option<SendMessageParams>, ) -> Result<bool>
Отвечает на запрос обратного вызова необязательным уведомлением и/или заменяющим сообщением.
Работает только если обновление имеет тип MessageCallback.
Sourcepub async fn answer_callback_raw(
&self,
notification: Option<&str>,
message_json: Option<Value>,
) -> Result<bool>
pub async fn answer_callback_raw( &self, notification: Option<&str>, message_json: Option<Value>, ) -> Result<bool>
Отвечает на запрос обратного вызова, передавая новое сообщение в виде сырого JSON.
Examples found in repository?
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}Sourcepub async fn edit_text(&self, new_text: &str) -> Result<()>
pub async fn edit_text(&self, new_text: &str) -> Result<()>
Редактирует текст текущего сообщения (только если update содержит message).
Sourcepub async fn edit_keyboard(&self, keyboard: InlineKeyboard) -> Result<()>
pub async fn edit_keyboard(&self, keyboard: InlineKeyboard) -> Result<()>
Заменяет inline-клавиатуру у текущего сообщения.
Sourcepub async fn edit_message(&self, params: EditMessageParams) -> Result<()>
pub async fn edit_message(&self, params: EditMessageParams) -> Result<()>
Общий метод редактирования сообщения.
Sourcepub async fn answer_callback_notification(
&self,
notification: &str,
) -> Result<bool>
pub async fn answer_callback_notification( &self, notification: &str, ) -> Result<bool>
Отвечает на callback только всплывающим уведомлением (не меняя сообщение).
Sourcepub async fn answer_callback_replace(
&self,
message: NewMessageBody,
) -> Result<bool>
pub async fn answer_callback_replace( &self, message: NewMessageBody, ) -> Result<bool>
Отвечает на callback, полностью заменяя сообщение (текст + клавиатура + вложения).
Sourcepub async fn answer_callback_edit_text(&self, new_text: &str) -> Result<bool>
pub async fn answer_callback_edit_text(&self, new_text: &str) -> Result<bool>
Отвечает на callback, изменяя только текст сообщения (клавиатура и вложения остаются).
Sourcepub async fn answer_callback_edit_keyboard(
&self,
keyboard: InlineKeyboard,
) -> Result<bool>
pub async fn answer_callback_edit_keyboard( &self, keyboard: InlineKeyboard, ) -> Result<bool>
Отвечает на callback, изменяя только клавиатуру.