maxbot 2.0.1

Автоматизация работы с чат-ботами на платформе MAX (max.ru)
Documentation
//! Сбор идентификаторов чатов через подписки (альтернатива устаревшему GET /chats).
//!
//! Бот подписывается на события добавления/удаления в чатах и хранит список chat_id.
//! По команде /list выводит все сохранённые идентификаторы.
//!
//! # Запуск
//! ```bash
//! export MAXBOT_TOKEN="ваш_токен"
//! cargo run --example subscription-chat-collector
//! ```

use maxbot::{Dispatcher, MaxClient};
use std::collections::HashSet;
use std::sync::Arc;
use tokio::sync::Mutex;

type ChatStore = Arc<Mutex<HashSet<i64>>>;

#[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);

    // Хранилище chat_id в памяти (в реальном проекте используйте БД)
    let store: ChatStore = Arc::new(Mutex::new(HashSet::new()));

    // Обработчик добавления бота в чат (групповой чат или канал)
    let store_clone = store.clone();
    dp.on_bot_added(move |ctx| {
        let store = store_clone.clone();
        async move {
            if let Some(chat_id) = ctx.chat_id() {
                let mut guard = store.lock().await;
                if guard.insert(chat_id) {
                    println!("✅ Бот добавлен в новый чат: {}", chat_id);
                    // Отправляем приветствие с chat_id
                    let _ = ctx.reply_text(&format!(
                        "Привет! Я бот. Мой ID в этом чате: `{}`",
                        chat_id
                    )).await;
                }
            }
            Ok(())
        }
    });

    // Обработчик запуска бота (личные сообщения или /start в группе)
    let store_clone = store.clone();
    dp.on_bot_started(move |ctx| {
        let store = store_clone.clone();
        async move {
            if let Some(chat_id) = ctx.chat_id() {
                let mut guard = store.lock().await;
                if guard.insert(chat_id) {
                    println!("✅ Бот запущен в чате: {}", chat_id);
                }
                // Отвечаем в личке или группе
                let _ = ctx.reply_text(&format!(
                    "Ваш chat_id: `{}`\nИспользуйте /list для просмотра всех сохранённых чатов.",
                    chat_id
                )).await;
            }
            Ok(())
        }
    });

    // Обработчик удаления бота из чата
    let store_clone = store.clone();
    dp.on_bot_removed(move |ctx| {
        let store = store_clone.clone();
        async move {
            if let Some(chat_id) = ctx.chat_id() {
                let mut guard = store.lock().await;
                if guard.remove(&chat_id) {
                    println!("❌ Бот удалён из чата: {}", chat_id);
                }
            }
            Ok(())
        }
    });

    // Команда /list – выводит все сохранённые chat_id
    let store_clone = store.clone();
    dp.on_command("/list", move |ctx| {
        let store = store_clone.clone();
        async move {
            let guard = store.lock().await;
            if guard.is_empty() {
                ctx.reply_text("Нет сохранённых чатов. Добавьте бота в группу или напишите /start в личку.")
                    .await?;
            } else {
                let mut response = String::from("Сохранённые chat_id:\n");
                for id in guard.iter() {
                    response.push_str(&format!("- {}\n", id));
                }
                ctx.reply_text(&response).await?;
            }
            Ok(())
        }
    });

    // Команда /clear – очищает хранилище (для тестирования)
    let store_clone = store.clone();
    dp.on_command("/clear", move |ctx| {
        let store = store_clone.clone();
        async move {
            let mut guard = store.lock().await;
            guard.clear();
            ctx.reply_text("Хранилище очищено.").await?;
            Ok(())
        }
    });

    println!("🔄 Бот запущен. Собираю chat_id из событий.");
    println!("📋 Используйте команду /list для просмотра всех сохранённых чатов.");
    println!("   /clear для очистки списка.");

    dp.start_polling().await;

    Ok(())
}