maxbot 0.7.6

Автоматизация работы с чат-ботами на платформе MAX (max.ru)
Documentation
//! Демо-бот «Информация о видео»
//!
//! Бот получает видеофайл, извлекает его токен,
//! запрашивает метаданные через API MAX и отправляет сообщение с информацией.
//!
//! # Запуск
//! ```bash
//! export MAXBOT_TOKEN="ваш_токен"
//! cargo run --example video-info-bot
//! ```

use maxbot::{Dispatcher, MaxClient, SendMessageParamsBuilder};
use std::time::Duration;
use tokio::time::timeout;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let bot = MaxClient::from_env().expect("MAXBOT_TOKEN not set");
    let mut dp = Dispatcher::new(bot);

    dp.on_command("/start", |ctx| async move {
        ctx.reply_markdown("Отправьте мне любое видео, и я расскажу о нём всё, что знаю! 🎬").await?;
        Ok(())
    });

    dp.on_message(|ctx| async move {
        let chat_id = ctx.chat_id().unwrap();

        // Ищем видео вложение
        let video_token = {
            let msg = match ctx.message() {
                Some(m) => m,
                None => return Ok(()),
            };
            let body = match &msg.body {
                Some(b) => b,
                None => return Ok(()),
            };
            let mut token = None;
            for att in &body.attachments {
                if let maxbot::AttachmentData::Video { payload } = att {
                    if let Some(tok) = &payload.token {
                        token = Some(tok.clone());
                        break;
                    }
                }
            }
            token
        };

        let video_token = match video_token {
            Some(tok) => tok,
            None => {
                ctx.reply_text("Это не видео (не удалось найти видео-вложение).").await?;
                return Ok(());
            }
        };

        // Получаем информацию с таймаутом
        let info = match timeout(Duration::from_secs(5), ctx.bot().get_video_info(&video_token)).await {
            Ok(Ok(i)) => i,
            Ok(Err(e)) => {
                ctx.reply_text(&format!("Ошибка получения информации: {}", e)).await?;
                return Ok(());
            }
            Err(_) => {
                ctx.reply_text("Сервер MAX не ответил вовремя.").await?;
                return Ok(());
            }
        };

        // Длительность приходит в миллисекундах
        let duration_secs = info.duration / 1000;
        let duration = Duration::from_secs(duration_secs as u64);
        let minutes = duration.as_secs() / 60;
        let seconds = duration.as_secs() % 60;
        let duration_str = if minutes > 0 {
            format!("{} мин {} сек", minutes, seconds)
        } else {
            format!("{} сек", seconds)
        };

        // Собираем доступные качества (без ссылок)
        let mut available_qualities = Vec::new();
        let quality_map = [
            ("mp4_1080", "1080p"),
            ("mp4_720", "720p"),
            ("mp4_480", "480p"),
            ("mp4_360", "360p"),
            ("mp4_240", "240p"),
            ("mp4_144", "144p"),
            ("hls", "HLS"),
        ];
        for (key, name) in quality_map.iter() {
            if info.urls.contains_key(*key) {
                available_qualities.push(*name);
            }
        }
        let qualities_text = if available_qualities.is_empty() {
            "неизвестно".to_string()
        } else {
            available_qualities.join(", ")
        };

        let text = format!(
            "🎥 **Информация о видео**\n\n\
             📐 Разрешение: {}×{}\n\
             ⏱ Длительность: {}\n\
             🎞 Качество: {}",
            info.width, info.height,
            duration_str,
            qualities_text
        );

        let builder = SendMessageParamsBuilder::new()
            .text(&text)
            .chat_id(chat_id)
            .format_markdown();
        if let Err(e) = ctx.bot().send_message_builder(builder).await {
            eprintln!("Ошибка отправки сообщения: {}", e);
        }
        Ok(())
    });

    println!("🎬 Видео-бот запущен. Отправьте видео, чтобы получить информацию.");
    dp.start_polling().await;
    Ok(())
}