maxbot 0.7.6

Автоматизация работы с чат-ботами на платформе MAX (max.ru)
Documentation
//! Бот, запрашивающий контактные данные через inline-кнопку.
//! При получении контакта разбирает vCard и отвечает с извлечённой информацией.

use maxbot::{Dispatcher, MaxClient, Attachment, InlineKeyboardButton, InlineKeyboardBuilder, SendMessageParamsBuilder};
use maxbot::vcard::parse_vcard;
use std::env;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let token = env::var("MAXBOT_TOKEN").expect("MAXBOT_TOKEN not set");
    let real_token = env::var("MAXBOT_REAL_TOKEN").unwrap_or_else(|_| token.clone());

    let mut check_hash = true;
    let mut client = MaxClient::new(token);
    if let Ok(proxy_url) = env::var("MAXBOT_PROXY") {
        client.set_base_url(proxy_url);
        if real_token.starts_with("Basic ") {
            check_hash = false;
        }
    }

    if !check_hash {
        println!("Токен бота подменён. Требуется указать MAXBOT_REAL_TOKEN для проверки номера телефона.");
    }

    let bot = client;
    let mut dp = Dispatcher::new(bot);

    // Команда /start – отправляем кнопку запроса контакта
    dp.on_command("/start", |ctx| async move {
        let chat_id = ctx.chat_id().unwrap();
        let keyboard = InlineKeyboardBuilder::new()
            .button(InlineKeyboardButton::request_contact("Передать 🔀"))
            .build()?;
        let body = SendMessageParamsBuilder::new()
            .text("Предоставьте свои данные")
            .chat_id(chat_id)
            .attachment(Attachment::inline_keyboard(keyboard))
            .build();
        ctx.bot().send_message(body).await?;
        Ok(())
    });

    // Подготовка токена проверки для передачи в замыкание
    let check_bytes = real_token.into_bytes();

    // Обработка входящего контакта
    dp.on_message(move |ctx| {
        let token_bytes = check_bytes.clone();
        async move {
            let msg = match ctx.message() {
                Some(m) => m,
                None => return Ok(()),
            };
            let attachments = match &msg.body {
                Some(b) => &b.attachments,
                None => return Ok(()),
            };
            for att in attachments {
                if let maxbot::AttachmentData::Contact { payload } = att {
                    let mut response = String::from("Вы предоставили следующую информацию:\n");
                    let mut has_phone = false;

                    // Разбор vCard, если есть
                    if let Some(vcf) = &payload.vcf_info {
                        match parse_vcard(vcf) {
                            Ok(card) => {
                                if let Some(fn_) = card.get_formatted_name() {
                                    response.push_str(&format!("Имя: {}\n", fn_));
                                }
                                for phone in card.get_phone_numbers() {
                                    response.push_str(&format!("Телефон: {}\n", phone));
                                    has_phone = !phone.is_empty();
                                }
                                for email in card.get_emails() {
                                    response.push_str(&format!("Email: {}\n", email));
                                }
                            }
                            Err(e) => response.push_str(&format!("Ошибка чтения vCard: {}\n", e)),
                        }
                    } else {
                        if let Some(name) = &payload.name {
                            response.push_str(&format!("Имя: {}\n", name));
                        }
                        if let Some(phone) = &payload.vcf_phone {
                            response.push_str(&format!("Телефон: {}\n", phone));
                            has_phone = !phone.is_empty();
                        }
                    }

                    // Проверка контрольной суммы
                    if check_hash && has_phone {
                        if payload.verify_hash(&token_bytes) {
                            response.push_str("✅ Номер телефона подтверждён.\n");
                        } else {
                            response.push_str("❌ Номер телефона не подтверждён.\n");
                        }
                    }

                    ctx.reply_text(&response).await?;
                    return Ok(());
                }
            }
            Ok(())
        }
    });

    println!("Бот запущен. Отправьте команду /start для запроса контактных данных.");
    dp.start_polling().await;
    Ok(())
}