maxbot
Rust-библиотека для создания ботов на платформе MAX
API: https://dev.max.ru/docs-api
Библиотека предоставляет удобный, асинхронный и типобезопасный интерфейс ко всем методам API MAX: отправка сообщений (с разбиением длинного текста), загрузка файлов, управление чатами и участниками, inline-клавиатуры, обработка callback-запросов, webhook/long polling, диспетчер событий и многое другое.
Важно: API MAX доступен только для российских юридических лиц и индивидуальных предпринимателей. Для получения токена бота обратитесь к документации MAX https://business.max.ru/self.
📦 Установка
Добавьте в Cargo.toml:
[dependencies]
maxbot = "0.7"
tokio = { version = "1", features = ["full"] }
🚀 Быстрый старт
1. Пропишите полученный токен доступа к боту
export MAXBOT_TOKEN="ваш_токен"
2. Создайте клиент работы с MAX
use maxbot::MaxClient;
let client = MaxClient::from_env().expect("Токен не найден");
Перед началом работы убедитесь, что бот активен и токен корректен.
let me = client.get_me().await?;
println!("is_bot: {}", me.user.is_bot);
3. Узнайте идентификатор своей группы (chat_id)
Для отправки сообщений в группу необходимо знать её числовой идентификатор chat_id.
Алгоритм:
- Создайте группу в MAX (через приложение или веб-интерфейс).
- Добавьте бота в эту группу.
- Запросите список чатов, в которых участвует бот, и найдите нужный по полю
title.
// Получаем первые 10 чатов
let (chats, _next_marker) = client.get_chats(Some(10), None).await?;
for chat in chats {
println!("Chat ID: {}, Title: {:?}, Participants: {}",
chat.chat_id,
chat.title.as_deref().unwrap_or("без названия"),
chat.participants_count);
}
Вы увидите список групп, где присутствует бот. Найдите нужную по заголовку (title) и запомните chat_id.
Это число нужно будет подставлять в параметры chat_id при вызове SendMessageParamsBuilder::new().chat_id(...).
4. Отправьте простое сообщение через построитель
use maxbot::{MaxClient, SendMessageParamsBuilder};
let mids = client.send_message_builder(
SendMessageParamsBuilder::new()
.text("Привет, чат!")
.chat_id(chat_id)
).await?;
println!("Сообщение отправлено, ID: {:?}", mids);
5. Можно использовать диспетчер для обработки событий
use maxbot::{Dispatcher, MaxClient, SendMessageParamsBuilder};
#[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 {
if let Some(text) = ctx.text() {
ctx.reply_text(text).await?;
}
Ok(())
});
dp.start_polling().await;
Ok(())
}
6. Отправьте сообщение с inline-клавиатурой и обработайте нажатие
use maxbot::{
MaxClient, SendMessageParamsBuilder, Attachment,
InlineKeyboardBuilder, InlineKeyboardButton, GetUpdatesParams,
};
// Отправка сообщения с клавиатурой
let keyboard = InlineKeyboardBuilder::new()
.button(InlineKeyboardButton::link("е1.ru", "https://www.e1.ru"))
.button(InlineKeyboardButton::callback("Щёлк!", "my_payload"))
.button(InlineKeyboardButton::clipboard("Скопировать код", "SECRET"))
.build()?;
let mids = client.send_message_builder(
SendMessageParamsBuilder::new()
.text("Выбери действие:")
.chat_id(chat_id)
.attachment(Attachment::inline_keyboard(keyboard))
).await?;
// Ожидание callback
let params = GetUpdatesParams {
marker: None,
limit: Some(10),
timeout: Some(30),
types: vec!["message_callback".to_string()],
};
let (updates, _) = client.get_updates(params).await?;
for update in updates {
if let Some(cb) = update.callback {
// Ответ на нажатие: заменяем сообщение и показываем уведомление
client.answer_callback_query(&cb.callback_id, new_message_json, Some("Нажато!")).await?;
}
}
7. Отправка файла (изображения, видео, аудио, документа)
use maxbot::{Attachment, SendMessageParamsBuilder};
// Из локального файла
let builder = SendMessageParamsBuilder::new()
.text("Вот изображение")
.chat_id(chat_id)
.attachment(Attachment::image_local("cat.jpg"));
// Или по прямому URL
let builder = SendMessageParamsBuilder::new()
.text("Картинка по ссылке")
.chat_id(chat_id)
.attachment(Attachment::image_url("https://example.com/image.jpg"));
client.send_message_builder(builder).await?;
8. Настройка прокси
Библиотека поддерживает использование прокси-сервера, что полезно для обхода блокировок МАКС в других странах или использования корпоративных шлюзов.
Установка прокси для всех экземпляров MaxClient:
maxbot::set_global_proxy(Some("http://user:pass@proxy.example.com:8080"));
// или отключить:
maxbot::set_global_proxy(None);
Если вам нужно определить прокси только для конкретного клиента, используйте метод `set_proxy`:
```rust
let mut client = MaxClient::from_env().unwrap();
client.set_proxy(Some("socks5://127.0.0.1:1080"));
Поддерживаются схемы http://, https://, socks5://. Формат строки прокси соответствует reqwest::Proxy::all().
🍃 Простые методы отправки
Для самых частых задач есть удобные сокращённые методы (типаж MaxClientSimpleExt):
use maxbot::{MaxClient, MaxClientSimpleExt};
let client = MaxClient::from_env().unwrap();
let chat_id = 123456789;
// Простой текст
client.send_plain_text_to_chat(chat_id, "Привет!").await?;
// Markdown
client.send_markdown_to_chat(chat_id, "**bold** и *italic*").await?;
// HTML
client.send_html_to_chat(chat_id, "<b>bold</b> и <i>italic</i>").await?;
// Изображение с подписью
client.send_image_to_chat(chat_id, "cat.jpg".as_ref(), Some("Кот")).await?;
// Видео
client.send_video_to_chat(chat_id, "video.mp4".as_ref(), None).await?;
// Стикер
client.send_sticker_to_chat(chat_id, "154ed15bb").await?;
// Геолокация
client.send_location_to_chat(chat_id, 55.7558, 37.6176).await?;
// Клавиатура
let kb = InlineKeyboardBuilder::new()
.button(InlineKeyboardButton::callback("Нажми", "data"))
.build()?;
client.send_keyboard_to_chat(chat_id, "Выбери:", kb).await?;
📎 Inline‑клавиатура
Библиотека поддерживает два подхода к построению клавиатур:
- Явное управление строками – методы
row(),button(),buttons(),new_row(). - Автоматическое распределение –
push_button()иpush_buttons().
Автоматическое распределение соблюдает ограничения MAX:
- до 7 обычных кнопок (
callback,message,clipboard) в строке, - до 3 кнопок в строке, если среди них есть хотя бы одна специальная (
link,open_app,request_contact,request_geo_location).
При превышении ограничения автоматически создаётся новая строка.
use maxbot::{InlineKeyboardBuilder, InlineKeyboardButton};
let keyboard = InlineKeyboardBuilder::new()
.push_button(InlineKeyboardButton::callback("Да", "yes"))
.push_button(InlineKeyboardButton::callback("Нет", "no"))
.push_button(InlineKeyboardButton::callback("Возможно", "maybe"))
.push_button(InlineKeyboardButton::link("Помощь", "https://xn--90aivcdt6dxbc.xn--p1ai/")) // Новая строка
.push_button(InlineKeyboardButton::callback("Настройки", "settings")) // Добавится в ту же новую строку
.build()?;
🔒 Проксирование запросов через nginx (безопасное хранение токенов)
По умолчанию библиотека обращается напрямую к API MAX (https://platform-api.max.ru), и токен бота передаётся в заголовке Authorization каждого запроса. Это означает, что любой разработчик, имеющий доступ к исходному коду или переменным окружения, может увидеть токен.
Проблема: Токен бота — секретная информация. Если вы не хотите раскрывать его всем разработчикам или хранить в файлах конфигурации, можно развернуть собственный прокси-сервер (например, на базе nginx), который будет:
- Принимать запросы от ботов с базовой HTTP-аутентификацией (логин/пароль).
- Подменять заголовок
Authorizationна реальный токен бота MAX. - Проксировать запросы к API MAX.
Преимущества:
- Разработчики работают только со своим личным логином/паролем, не зная токен бота.
- При увольнении сотрудника или при завершении им работы над проектом достаточно удалить его учётную запись на прокси-сервере.
- Токен бота хранится только на сервере с nginx.
Пример настройки nginx
Предположим, вы хотите использовать домен https://max.my-russian-company.ru как прокси.
- Добавьте пользователей в файл
.htpasswd:
htpasswd /etc/nginx/.htpasswd ivanov.i
htpasswd /etc/nginx/.htpasswd petrov.p
- Создайте конфигурацию nginx (например,
/etc/nginx/sites-available/max-proxy):
# Соответствие имён пользователей из .htpasswd токенам бота
map $remote_user $backend_token {
default "";
"ivanov.i" "реальный_токен_бота_1";
"petrov.p" "реальный_токен_бота_2";
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name max.my-russian-company.ru;
ssl_certificate /etc/letsencrypt/live/my-russian-company.ru-0001/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/my-russian-company.ru-0001/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
auth_basic "Restricted access";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass https://platform-api.max.ru;
proxy_ssl_verify off;
proxy_set_header Host platform-api.max.ru;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Authorization "$backend_token";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
}
- Активируйте конфигурацию и перезапустите nginx:
ln -s /etc/nginx/sites-available/max-proxy /etc/nginx/sites-enabled/
nginx -t
systemctl restart nginx
Использование в коде
В вашем приложении нужно указать библиотеке использовать собственный базовый URL (адрес вашего прокси-сервера) и передавать логин/пароль через стандартный заголовок Authorization: Basic .... Библиотека maxbot позволяет это сделать:
use maxbot::{MaxClient, set_global_base_url};
// 1. Устанавливаем глобальный базовый URL (все клиенты будут использовать прокси)
set_global_base_url("https://max.my-russian-company.ru");
// 2. Создаём клиент с токеном базовой авторизации HTTP
let client = MaxClient::new("Basic aXZhbm92Lmk6bXlfc2VjcmV0X3Bhc3N3b3JkMQ==");
// 3. Все дальнейшие вызовы автоматически используют прокси.
let me = client.get_me().await?;
Важно: В этом режиме токен бота нигде в коде не фигурирует — он остаётся только на сервере nginx. Разработчики используют свои личные учётные данные.
Если вам нужно переопределить базовый URL только для конкретного клиента, используйте метод set_base_url:
let mut client = MaxClient::new("Basic cGV0cm92LnA6bXlfc2VjcmV0X3Bhc3N3b3JkMg==");
client.set_base_url("https://max.my-russian-company.ru");
📚 Полная документация API
Все методы сгруппированы в impl MaxClient:
| Модуль | Методы |
|---|---|
| bot | get_me() – информация о боте |
| chats | get_chat, get_chats, update_chat, delete_chat, leave_chat, get_chat_members, get_chat_members_by_ids, get_chat_admins, add_chat_admin, add_chat_admins, remove_chat_admin, remove_chat_admins, add_chat_members, remove_chat_member, get_bot_member, send_chat_action, get_pinned_message, pin_message, unpin_message |
| messages | send_message (и send_message_builder), forward_message, delete_message, delete_messages, get_messages, edit_message, edit_multimessage |
| simple | send_plain_text_to_chat, send_markdown_to_chat, send_html_to_chat, send_image_to_chat, send_video_to_chat, send_audio_to_chat, send_file_to_chat, send_sticker_to_chat, send_location_to_chat, send_contact_to_chat, send_share_to_chat, send_keyboard_to_chat, send_plain_text_to_user, send_markdown_to_user, send_html_to_user, send_image_to_user, send_video_to_user, send_audio_to_user, send_file_to_user, send_sticker_to_user, send_location_to_user, send_contact_to_user, send_share_to_user, send_keyboard_to_user |
| videos | get_video_info, download_video |
| uploads | upload_file – загрузка файла и получение токена |
| updates | get_updates, answer_callback_query |
| subscriptions | get_subscriptions, create_subscription, delete_subscription |
Все методы асинхронны, используют reqwest и автоматически соблюдают лимит 30 запросов в секунду (rate limiting). Длинные тексты разбиваются на фрагменты до 4000 символов с сохранением целостности слов и предложений.
🧪 Примеры
Полные примеры в папке examples/:
check-bot.rs– проверка работоспособности бота (выводитtrueилиfalse)me-demo.rs– информация о ботеecho-bot.rs– простой эхо-ботdispatcher-task.rs- демонстрация периодических задачfruits-bot.rs– демонстрация бота с обработкой нажатия на кнопкиwebhook-bot.rs– пример webhook-бота на Actix-webvideo-info-bot.rs– демонстрация бота с получением информации о видеоget-chats-demo.rs– получение списка чатов и их идентификаторовget-chat-demo.rs– получение информации о групповом чатеupdate-chat-demo.rs– изменение информации о групповом чате (название, иконка, закрепление сообщения)delete-chat-demo.rs– удаление группового чата (с обработкой ошибок)leave-chat-demo.rs– выход бота из чата (с фиктивным ID и обработкой ошибки)get-bot-member-demo.rs– информация о членстве бота в групповом чате (права, дата вступления и т.д.)get-chat-members-demo.rs– получение списка участников чата (постранично или по ID)get-chat-admins-demo.rs– получение списка администраторов чата с правамиadd-chat-admin-demo.rs– назначение одного или нескольких администраторовremove-chat-admin-demo.rs– снятие прав администратора с пользователейget-messages-demo.rs– получение истории сообщений чата (если доступно)send-text-demo.rs– отправка простого текстаsend-long-text-demo.rs– разбиение длинного текстаsend-picture-demo.rs– отправка изображенияsend-picture-only-demo.rs– отправка изображения без текстаsend-video-demo.rs– отправка видеоsend-audio-demo.rs– отправка аудиоsend-file-any-demo.rs– отправка произвольного файлаupload-file-demo.rs– предварительная загрузка файла и использование токенаget-video-info-demo.rs– получение метаданных видеоsend-inline-keyboard-demo.rs– сообщение с клавиатуройcombined-attachments-demo.rs– все типы вложений (стикер, контакт, гео, ссылка, клавиатура)send-chat-actions-demo.rs– демонстрация отправки действий бота (набор текста, отправка медиа и т.д.)pin-message-demo.rs– закрепление и открепление сообщенияforward-demo.rs– пересылка сообщенияdelete-messages-demo.rs– отправка и удалениеedit-message-demo.rs– редактирование сообщенияfull-demo.rs– комплексная демонстрация большинства возможностейstress-test.rs– стресс-тест ограничения RPS (отправка сообщений до ошибки 429)
Запуск примера:
export MAXBOT_TOKEN="ваш_токен"
export CHAT_ID=-123456789
cargo run --example stress-test -- 10
Для webhook-бота:
cargo run --example webhook-bot --features webhook
✅ Что уже реализовано
- Базовая HTTP-обёртка с rate limiting (30 RPS)
- Получение информации о боте (
/me) - Управление чатами: получение, обновление (название, иконка, закрепление, уведомления), удаление, выход
- Управление участниками: получение списка (постранично и по ID), добавление, удаление
- Полное управление администраторами: получение списка, добавление (одного или нескольких с правами и псевдонимом), снятие прав (одного или нескольких)
- Отправка сообщений (текст, markdown, html, reply)
- Разбиение длинного текста на фрагменты (сохранение целостности слов/предложений)
- Отправка файлов (изображения, видео, аудио, документы) — из файла, по токену или по прямому URL
- Предварительная загрузка файлов и повторное использование токенов
- Получение информации о видео (
/videos/{token}) - Пересылка сообщений
- Удаление сообщений (одиночное и массовое)
- Получение истории сообщений чата (
/messages) - Редактирование сообщений (одиночных и многофрагментных)
- Закрепление/открепление сообщений
- Отправка действий бота в чате (typing_on, sending_photo, …)
- Long polling (
/updates) и диспетчер событий (с фильтрами,on_command,on_message,on_callbackи т.д.) - Обработчики старта (
on_start) для инициализации перед long polling - Периодические задачи (
task) с настраиваемым интервалом - Ответ на callback-запросы (inline-кнопки) – замена сообщения и уведомления
- Webhook-подписки (создание, удаление, список) + готовый сервер на Actix-web (параметр сборки
webhook) - Удобные Builder’ы для параметров сообщений и клавиатур
- Кнопка
clipboard(копирование в буфер обмена без callback) - Автоматическое распределение кнопок в inline-клавиатуре с соблюдением ограничений MAX (методы
push_button,push_buttons) - Проверка клавиатуры при сборке (максимум строк, кнопок в строке, общее количество)
- Простые методы отправки (типаж
MaxClientSimpleExt) - Поддержка всех типов вложений: стикеры, контакты, геолокация, share, файлы, изображения, видео, аудио
📄 Лицензия
Проект распространяется под лицензией MIT.
🤝 Вклад
Принимаются pull request’ы. Для крупных изменений сначала откройте issue для обсуждения.