# maxbot
[](https://crates.io/crates/maxbot)
[](https://docs.rs/maxbot)
[](https://opensource.org/licenses/MIT)
> Rust-библиотека для создания ботов на платформе [MAX](https://max.ru/)
> API: https://dev.max.ru/docs-api
Библиотека предоставляет удобный, асинхронный и типобезопасный интерфейс ко всем методам API MAX: отправка сообщений (с разбиением длинного текста), загрузка файлов, управление чатами и участниками, inline-клавиатуры, обработка callback-запросов, webhook/long polling, диспетчер событий и многое другое.
**Важно:** API MAX доступен только для российских юридических лиц и индивидуальных предпринимателей. Для получения токена бота обратитесь к документации MAX <https://business.max.ru/self>.
---
## 📦 Установка
Добавьте в `Cargo.toml`:
```toml
[dependencies]
maxbot = "2.0"
tokio = { version = "1", features = ["full"] }
```
---
## 🚀 Быстрый старт
### 1. Пропишите полученный токен доступа к боту
```bash
export MAXBOT_TOKEN="ваш_токен"
```
### 2. Создайте клиент работы с MAX
```rust
use maxbot::MaxClient;
let client = MaxClient::from_env().expect("Токен не найден");
```
Перед началом работы убедитесь, что бот активен и токен корректен.
```rust
let me = client.get_me().await?;
println!("is_bot: {}", me.user.is_bot);
```
### 3. Узнайте идентификатор своей группы (chat\_id)
Для отправки сообщений в группу необходимо знать её числовой идентификатор `chat_id`.
**Важное обновление:** с июня 2026 года метод `GET /chats` больше не поддерживается.
Получить `chat_id` можно только через события, которые приходят при добавлении бота в чат или при старте бота.
Алгоритм получения `chat_id`:
1. Создайте подписку на события (см. раздел «Webhook» или используйте Long Polling).
2. Подпишитесь на события `bot_added` и/или `bot_started` (в `update_types`).
3. При получении события сохраните `chat_id` из поля события.
4. Используйте сохранённый `chat_id` для всех вызовов API.
Вот минимальный пример бота, который при добавлении в чат выводит его `chat_id` и сохраняет его:
```rust
use maxbot::{Dispatcher, MaxClient};
#[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
dp.on_bot_added(|ctx| async move {
if let Some(chat_id) = ctx.chat_id() {
println!("Бот добавлен в чат с ID: {}", chat_id);
// Здесь можно сохранить chat_id в БД или файл
ctx.reply_text(&format!("Привет! Мой ID в этом чате: {}", chat_id)).await?;
}
Ok(())
});
// Также можно обработать команду /start, чтобы получить chat_id в личке
dp.on_command("/start", |ctx| async move {
if let Some(chat_id) = ctx.chat_id() {
ctx.reply_text(&format!("Ваш chat_id: {}", chat_id)).await?;
} else {
ctx.reply_text("Не удалось определить chat_id").await?;
}
Ok(())
});
println!("Бот запущен. Нажмите Ctrl+C для остановки.");
dp.start_polling().await;
Ok(())
}
```
Если вы используете Webhook, обработка событий аналогична – в обработчике `BotAdded` или `BotStarted` вы получите `chat_id`.
> **Важно:** Задача сбора, хранения и поддержки идентификаторов теперь лежит на разработчике. Вы можете использовать любое хранилище (файл, SQLite, PostgreSQL, Redis и т.д.).
### 4. Отправьте простое сообщение через построитель
```rust
use maxbot::{MaxClient, SendMessageParamsBuilder};
let mids = client.send_message_builder(
SendMessageParamsBuilder::new()
.text("Привет, чат!")
.chat_id(chat_id)
).await?;
println!("Сообщение отправлено, ID: {:?}", mids);
```
### 5. Можно использовать диспетчер для обработки событий
```rust
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-клавиатурой и обработайте нажатие
```rust
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. Отправка файла (изображения, видео, аудио, документа)
```rust
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`:
```rust
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()`.
### 9. 🔒 Настройка SSL-сертификатов
С **25 мая 2026** года API MAX требует использования сертификата, выпущенного
Минцифры России. Если ваш сервер не доверяет этому сертификату (например, из-за
устаревшего списка корневых сертификатов), вы можете временно отключить проверку
SSL-сертификатов.
> **Важно:** Отключение проверки SSL снижает безопасность соединения.
> Используйте этот способ только для отладки или пока не сможете обновить
> сертификаты на вашей системе.
### Глобальное отключение (для всех клиентов)
```rust
use maxbot::set_global_ignore_ssl_errors;
set_global_ignore_ssl_errors(true);
let client = MaxClient::from_env()?; // будет использовать игнорирование SSL
```
### Локальное отключение (для конкретного клиента)
```rust
let mut client = MaxClient::new("token");
client.set_ignore_ssl_errors(true); // только для этого клиента
```
Если вы хотите вернуться к стандартной проверке, вызовите те же методы с `false`.
---
***Обратите внимание***: базовый URL API изменён на https://platform-api2.max.ru.
Библиотека использует его по умолчанию. Если вам нужен старый адрес,
используйте:
```rust
set_global_base_url("https://platform-api.max.ru");
// или client.set_base_url("https://platform-api.max.ru")
```
---
## 🍃 Простые методы отправки
Для самых частых задач есть удобные сокращённые методы (типаж `MaxClientSimpleExt`):
```rust
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`).
При превышении ограничения автоматически создаётся новая строка.
```rust
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` как прокси.
1. **Добавьте пользователей** в файл `.htpasswd`:
```bash
htpasswd /etc/nginx/.htpasswd ivanov.i
htpasswd /etc/nginx/.htpasswd petrov.p
```
2. **Создайте конфигурацию nginx** (например, `/etc/nginx/sites-available/max-proxy`):
```nginx
# Соответствие имён пользователей из .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;
}
}
```
3. **Активируйте конфигурацию** и перезапустите nginx:
```bash
ln -s /etc/nginx/sites-available/max-proxy /etc/nginx/sites-enabled/
nginx -t
systemctl restart nginx
```
#### Использование в коде
В вашем приложении нужно указать библиотеке использовать собственный базовый URL (адрес вашего прокси-сервера) и передавать логин/пароль через стандартный заголовок `Authorization: Basic ...`. Библиотека `maxbot` позволяет это сделать:
```rust
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`:
```rust
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` (получение чата по ID), `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` <br> **Примечание:** метод `get_chats` (получение списка всех чатов) **устарел и удалён** с июня 2026. Используйте подписки для получения событий с `chat_id`. |
| **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 символов с сохранением целостности слов и предложений.
---
## 📡 Обработка событий (Dispatcher)
`Dispatcher` позволяет регистрировать обработчики для всех типов событий, поддерживаемых API MAX. Ниже приведена полная таблица соответствия:
| Событие (из документации MAX) | Фильтр в библиотеке | Метод регистрации | Описание |
|--------------------------------------|--------------------------|-----------------------------|----------|
| `message_created` | `Message` | `on_message` | Новое сообщение или публикация |
| `message_edited` | `EditedMessage` | `on_edited_message` | Редактирование сообщения |
| `message_removed` | `MessageRemoved` | `on_message_removed` | Удаление сообщения |
| `message_callback` | `Callback` | `on_callback` | Нажатие на inline-кнопку |
| `bot_started` | `BotStarted` | `on_bot_started` | Пользователь начал/возобновил диалог с ботом |
| `bot_stopped` | `BotStopped` | `on_bot_stopped` | Пользователь остановил бота в настройках |
| `bot_added` | `BotAdded` | `on_bot_added` | Бот добавлен в чат или канал |
| `bot_removed` | `BotRemoved` | `on_bot_removed` | Бот удалён из чата или канала |
| `user_added` | `UserAdded` | `on_user_added` | Пользователь добавлен в чат/канал |
| `user_removed` | `UserRemoved` | `on_user_removed` | Пользователь удалён/покинул чат/канал |
| `chat_title_changed` | `ChatTitleChanged` | `on_chat_title_changed` | Изменено название чата или канала |
| `dialog_cleared` | `DialogCleared` | `on_dialog_cleared` | Очистка истории диалога с ботом |
| `dialog_muted` | `DialogMuted` | `on_dialog_muted` | Отключение уведомлений в диалоге |
| `dialog_unmuted` | `DialogUnmuted` | `on_dialog_unmuted` | Включение уведомлений в диалоге |
| `dialog_removed` | `DialogRemoved` | `on_dialog_removed` | Удаление диалога с ботом |
| Произвольная команда (например, `/start`)| `Command(String)` | `on_command` | Фильтр по тексту команды |
| Конкретный payload callback'а | `CallbackPayload(String)`| `on_callback_payload` | Фильтр по полезной нагрузке кнопки |
| Любое событие | `Any` | `on` | Обработчик всех событий |
| Пользовательский фильтр | `Custom` | `on_filter` | Произвольный фильтр |
### Пример обработки всех типов событий
```rust
use maxbot::{Dispatcher, MaxClient};
let bot = MaxClient::from_env()?;
let mut dp = Dispatcher::new(bot);
// Новое сообщение
dp.on_message(|ctx| async move {
println!("Новое сообщение от {}", ctx.user_id().unwrap_or(0));
Ok(())
});
// Редактирование
dp.on_edited_message(|ctx| async move {
println!("Сообщение отредактировано");
Ok(())
});
// Удаление сообщения
dp.on_message_removed(|ctx| async move {
println!("Сообщение удалено");
Ok(())
});
// Callback
dp.on_callback(|ctx| async move {
let payload = ctx.update.callback().unwrap().payload;
println!("Нажата кнопка с payload: {}", payload);
Ok(())
});
// Бот добавлен в чат – сохраняем chat_id
dp.on_bot_added(|ctx| async move {
let chat_id = ctx.chat_id().unwrap();
println!("Бот добавлен в чат {}", chat_id);
// Сохраняем chat_id в хранилище
Ok(())
});
// Бот удалён из чата – удаляем chat_id
dp.on_bot_removed(|ctx| async move {
let chat_id = ctx.chat_id().unwrap();
println!("Бот удалён из чата {}", chat_id);
// Удаляем chat_id из хранилища
Ok(())
});
// Пользователь добавлен
dp.on_user_added(|ctx| async move {
println!("Пользователь добавлен в чат {}", ctx.chat_id().unwrap());
Ok(())
});
// Пользователь удалён
dp.on_user_removed(|ctx| async move {
println!("Пользователь удалён из чата {}", ctx.chat_id().unwrap());
Ok(())
});
// Изменение названия
dp.on_chat_title_changed(|ctx| async move {
if let Some(chat_id) = ctx.chat_id() {
println!("Название чата {} изменено", chat_id);
}
Ok(())
});
// События диалога (личные сообщения)
dp.on_bot_stopped(|ctx| async move {
println!("Бот остановлен пользователем {}", ctx.user_id().unwrap());
Ok(())
});
dp.on_dialog_cleared(|ctx| async move {
println!("Диалог очищен пользователем {}", ctx.user_id().unwrap());
Ok(())
});
dp.on_dialog_muted(|ctx| async move {
println!("Уведомления отключены в диалоге с {}", ctx.user_id().unwrap());
Ok(())
});
dp.on_dialog_unmuted(|ctx| async move {
println!("Уведомления включены в диалоге с {}", ctx.user_id().unwrap());
Ok(())
});
dp.on_dialog_removed(|ctx| async move {
println!("Диалог удалён пользователем {}", ctx.user_id().unwrap());
Ok(())
});
dp.start_polling().await;
```
---
## 🧪 Примеры
Полные примеры в папке `examples/`:
- `check-bot.rs` – проверка работоспособности бота (выводит `true` или `false`)
- `me-demo.rs` – информация о боте
- `echo-bot.rs` – простой эхо-бот
- `dispatcher-task.rs` - демонстрация периодических задач
- `fruits-bot.rs` – демонстрация бота с обработкой нажатия на кнопки
- `webhook-bot.rs` – пример webhook-бота на Actix-web
- `video-info-bot.rs` – демонстрация бота с получением информации о видео
- `subscription-chat-collector.rs` – сбор `chat_id` через подписки
- `get-chat-demo.rs` – получение информации о групповом чате по известному ID
- `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)
Запуск примера:
```bash
export MAXBOT_TOKEN="ваш_токен"
export CHAT_ID=-123456789
cargo run --example stress-test -- 10
```
Для webhook-бота:
```bash
cargo run --example webhook-bot --features webhook
```
---
## 🌐 Webhook и подписки
### Создание подписки
Для получения обновлений через Webhook необходимо создать подписку:
```rust
use maxbot::MaxClient;
let client = MaxClient::from_env()?;
client.create_subscription(
"https://my-server.com/webhook",
vec!["message_created".to_string(), "bot_added".to_string()],
Some("my_secret")
).await?;
```
- URL должен быть **HTTPS** (порт 443 рекомендуется).
- Сертификат должен быть от **доверенного центра** (самоподписанные и HTTP больше не принимаются с 25 мая 2026).
### Запуск webhook-сервера
```rust
use maxbot::{Dispatcher, MaxClient, webhook::WebhookServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let bot = MaxClient::from_env().unwrap();
let mut dp = Dispatcher::new(bot);
dp.on_message(|ctx| async move {
ctx.reply_text("Hello from webhook!").await?;
Ok(())
});
WebhookServer::new(dp)
.secret("my_secret") // должен совпадать с переданным в create_subscription
.path("/webhook")
.serve("0.0.0.0:8443") // за прокси, который реализует HTTPS
.await
}
```
### Получение chat_id через подписки
Для получения идентификаторов чатов, в которые добавлен бот, необходимо:
1. Создать подписку с `update_types`, включающими `"bot_added"` и/или `"bot_started"`.
2. В обработчике события `BotAdded` или `BotStarted` извлечь `chat_id`.
3. Сохранить `chat_id` в своём хранилище (БД, файл, память).
4. Использовать сохранённые ID для вызовов API.
Пример:
```rust
use maxbot::{Dispatcher, MaxClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let bot = MaxClient::from_env()?;
let mut dp = Dispatcher::new(bot);
dp.on_bot_added(|ctx| async move {
if let Some(chat_id) = ctx.chat_id() {
println!("Новый чат: {}", chat_id);
// Здесь можно сохранить chat_id
}
Ok(())
});
// Также можно обрабатывать bot_started
dp.on_bot_started(|ctx| async move {
if let Some(chat_id) = ctx.chat_id() {
println!("Бот запущен в чате: {}", chat_id);
}
Ok(())
});
dp.start_polling().await;
Ok(())
}
```
---
## ✅ Что уже реализовано
- [x] Базовая HTTP-обёртка с rate limiting (30 RPS)
- [x] Получение информации о боте (`/me`)
- [x] Управление чатами: получение по ID, обновление (название, иконка, закрепление, уведомления), удаление, выход
- [x] Управление участниками: получение списка (постранично и по ID), добавление, удаление
- [x] Полное управление администраторами: получение списка, добавление (одного или нескольких с правами и псевдонимом), снятие прав (одного или нескольких)
- [x] Отправка сообщений (текст, markdown, html, reply)
- [x] Разбиение длинного текста на фрагменты (сохранение целостности слов/предложений)
- [x] Отправка файлов (изображения, видео, аудио, документы) — из файла, по токену или по прямому URL
- [x] Предварительная загрузка файлов и повторное использование токенов
- [x] Получение информации о видео (`/videos/{token}`)
- [x] Пересылка сообщений
- [x] Удаление сообщений (одиночное и массовое)
- [x] Получение истории сообщений чата (`/messages`)
- [x] Редактирование сообщений (одиночных и многофрагментных)
- [x] Закрепление/открепление сообщений
- [x] Отправка действий бота в чате (typing\_on, sending\_photo, ...)
- [x] Long polling (`/updates`) и диспетчер событий (с фильтрами, `on_command`, `on_message`, `on_callback` и т.д.)
- [x] Обработчики старта (`on_start`) для инициализации перед long polling
- [x] Периодические задачи (`task`) с настраиваемым интервалом
- [x] Ответ на callback-запросы (inline-кнопки) – замена сообщения и уведомления
- [x] Webhook-подписки (создание, удаление, список) + готовый сервер на Actix-web (параметр сборки `webhook`)
- [x] Удобные Builder'ы для параметров сообщений и клавиатур
- [x] Кнопка `clipboard` (копирование в буфер обмена без callback)
- [x] Автоматическое распределение кнопок в inline-клавиатуре с соблюдением ограничений MAX (методы `push_button`, `push_buttons`)
- [x] Проверка клавиатуры при сборке (максимум строк, кнопок в строке, общее количество)
- [x] Простые методы отправки (типаж `MaxClientSimpleExt`)
- [x] Поддержка всех типов вложений: стикеры, контакты, геолокация, share, файлы, изображения, видео, аудио
- [x] Полная поддержка всех событий API MAX (BotAdded, BotRemoved, UserAdded, UserRemoved, ChatTitleChanged и др.)
---
## 📄 Лицензия
Проект распространяется под лицензией MIT.
---
## 🤝 Вклад
Принимаются pull request'ы. Для крупных изменений сначала откройте issue для обсуждения.
---
## 🌐 Полезные ссылки
- [Документация API MAX](https://dev.max.ru/docs-api)
- [Репозиторий библиотеки](https://gitflic.ru/project/perdumonocle/maxbot)
- [Crates.io](https://crates.io/crates/maxbot)