dizvestnov / laravel-max-bot
Laravel package for MAX messenger Bot API
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.4
- illuminate/console: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/queue: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
Requires (Dev)
- laravel/pint: ^1.0
- mockery/mockery: ^1.6
- nunomaduro/larastan: ^3.0
- orchestra/testbench: ^10.0|^11.0
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0|^12.0
README
Laravel-пакет для работы с MAX messenger Bot API.
Совместимость
| Ветка | Laravel | PHP | Testbench |
|---|---|---|---|
| 1.x | 8, 9 | 7.4, 8.0 | 6, 7 |
| 3.x | 12, 13 | 8.2, 8.3, 8.4 | 10, 11 |
Установка
composer require dizvestnov/laravel-max-bot
Опубликовать конфиг:
php artisan vendor:publish --tag=max-bot-config
Добавить в .env:
MAX_BOT_TOKEN=ваш_токен_бота
Токен выдаётся при создании бота через @MaxBotFather в MAX.
Настройка
Файл config/max-bot.php после публикации:
return [ 'token' => env('MAX_BOT_TOKEN', ''), 'http' => [ 'base_uri' => env('MAX_BOT_BASE_URI', 'https://platform-api.max.ru'), 'timeout' => (int) env('MAX_BOT_TIMEOUT', 30), 'retry' => [ 'times' => 3, 'sleep' => 100, // базовая задержка в мс (экспоненциальный backoff) ], ], 'webhook' => [ 'secret' => env('MAX_BOT_WEBHOOK_SECRET', null), 'version' => env('MAX_BOT_WEBHOOK_VERSION', null), 'route' => [ 'enabled' => true, 'path' => env('MAX_BOT_WEBHOOK_PATH', 'max-bot/webhook'), 'middleware' => ['api'], ], ], 'queue' => [ 'enabled' => (bool) env('MAX_BOT_QUEUE_ENABLED', false), 'connection' => env('MAX_BOT_QUEUE_CONNECTION', null), 'queue' => env('MAX_BOT_QUEUE_NAME', 'default'), ], ];
Переменные окружения
| Переменная | По умолчанию | Описание |
|---|---|---|
MAX_BOT_TOKEN |
— | Токен бота (обязательно) |
MAX_BOT_BASE_URI |
https://platform-api.max.ru |
Базовый URL API |
MAX_BOT_TIMEOUT |
30 |
Таймаут HTTP-запросов (сек) |
MAX_BOT_WEBHOOK_SECRET |
null |
HMAC-секрет для проверки подписи |
MAX_BOT_WEBHOOK_PATH |
max-bot/webhook |
URL-путь для вебхука |
MAX_BOT_QUEUE_ENABLED |
false |
Обрабатывать обновления через очередь |
MAX_BOT_QUEUE_CONNECTION |
null |
Соединение очереди (null = дефолтное) |
MAX_BOT_QUEUE_NAME |
default |
Имя очереди |
Отправка сообщений
Fluent-builder (рекомендуется)
use Dizvestnov\LaravelMaxBot\Messages\OutgoingMessage; // Простое текстовое сообщение пользователю OutgoingMessage::create('Привет!') ->to($userId) ->send(); // Сообщение в чат OutgoingMessage::create('Всем привет!') ->inChat($chatId) ->send(); // Markdown-форматирование OutgoingMessage::create('**Жирный** и _курсив_') ->to($userId) ->markdown() ->send(); // HTML-форматирование OutgoingMessage::create('<b>Жирный</b>') ->to($userId) ->html() ->send(); // Ответ на конкретное сообщение OutgoingMessage::create('Ответ') ->to($userId) ->replyTo($messageId) ->send();
Через фасад (низкоуровневый доступ)
use Dizvestnov\LaravelMaxBot\Facades\MaxBot; MaxBot::sendMessage([ 'recipient' => ['user_id' => $userId], 'text' => 'Привет!', ]);
Клавиатуры и кнопки
Типы кнопок
| Класс | Описание |
|---|---|
CallbackButton |
Callback-кнопка с payload |
LinkButton |
Ссылка на URL |
RequestContactButton |
Запрос контакта пользователя |
RequestGeoLocationButton |
Запрос геолокации |
MessageButton |
Кнопка, отправляющая текст в чат |
ClipboardButton |
Копирует текст в буфер обмена |
Пример клавиатуры
use Dizvestnov\LaravelMaxBot\Keyboard; use Dizvestnov\LaravelMaxBot\Buttons\CallbackButton; use Dizvestnov\LaravelMaxBot\Buttons\LinkButton; use Dizvestnov\LaravelMaxBot\Buttons\RequestContactButton; use Dizvestnov\LaravelMaxBot\Buttons\RequestGeoLocationButton; $keyboard = Keyboard::make() ->row( CallbackButton::make('Да', 'answer_yes'), CallbackButton::make('Нет', 'answer_no'), ) ->row( LinkButton::make('Наш сайт', 'https://example.com'), ) ->row( RequestContactButton::make('Поделиться контактом'), RequestGeoLocationButton::make('Отправить геолокацию'), ); OutgoingMessage::create('Выберите вариант:') ->to($userId) ->withKeyboard($keyboard) ->send();
Вебхуки
1. Настройка маршрута
Маршрут регистрируется автоматически. По умолчанию: POST /max-bot/webhook.
Изменить путь через .env:
MAX_BOT_WEBHOOK_PATH=my-bot/updates
2. Защита подписью (рекомендуется)
MAX_BOT_WEBHOOK_SECRET=ваш_секрет
Middleware VerifyMaxBotSignature проверяет HMAC-подпись каждого запроса и возвращает 403 при несовпадении.
3. Установить URL вебхука
php artisan max-bot:webhook:set https://yourdomain.com/max-bot/webhook
4. Обработка событий
Зарегистрируйте слушатели в EventServiceProvider:
use Dizvestnov\LaravelMaxBot\Events\MessageReceived; use Dizvestnov\LaravelMaxBot\Events\CallbackReceived; use Dizvestnov\LaravelMaxBot\Events\BotStarted; protected $listen = [ MessageReceived::class => [ App\Listeners\HandleMessage::class, ], CallbackReceived::class => [ App\Listeners\HandleCallback::class, ], BotStarted::class => [ App\Listeners\WelcomeNewUser::class, ], ];
Пример слушателя:
namespace App\Listeners; use Dizvestnov\LaravelMaxBot\Events\MessageReceived; use Dizvestnov\LaravelMaxBot\Messages\OutgoingMessage; class HandleMessage { public function handle(MessageReceived $event): void { $text = $event->getText(); $userId = $event->getSenderId(); OutgoingMessage::create("Вы написали: {$text}") ->to($userId) ->send(); } }
5. Доступные события
| Класс | Когда срабатывает |
|---|---|
MessageReceived |
Получено новое сообщение |
MessageEdited |
Сообщение отредактировано |
MessageRemoved |
Сообщение удалено |
CallbackReceived |
Нажата callback-кнопка |
BotStarted |
Пользователь запустил бота |
BotAdded |
Бот добавлен в чат |
BotRemoved |
Бот удалён из чата |
UserAdded |
Пользователь добавлен в чат |
UserRemoved |
Пользователь удалён из чата |
ChatTitleChanged |
Изменено название чата |
Методы, доступные во всех событиях (наследуются от MaxBotEvent):
$event->getUpdateType(); // тип обновления $event->getChatId(); // ID чата (или null) $event->getTimestamp(); // unix-timestamp $event->update; // исходный массив обновления
MessageReceived дополнительно:
$event->getText(); // текст сообщения $event->getSenderId(); // ID отправителя $event->getMessage(); // полный массив сообщения
6. Обработка через очередь
MAX_BOT_QUEUE_ENABLED=true MAX_BOT_QUEUE_CONNECTION=redis MAX_BOT_QUEUE_NAME=bot
Long Polling
Для локальной разработки или простых сценариев без публичного URL:
php artisan max-bot:poll
Команда получает обновления циклически через API и диспатчит те же события, что и вебхук.
Управление состоянием разговора
StateManager хранит состояние и данные пользователя в кэше Laravel.
use Dizvestnov\LaravelMaxBot\Conversation\StateManager; class RegistrationListener { public function __construct(private StateManager $state) {} public function handle(MessageReceived $event): void { $userId = $event->getSenderId(); $step = $this->state->getState($userId); if ($step === null) { $this->state->setState($userId, 'ask_name'); OutgoingMessage::create('Как вас зовут?')->to($userId)->send(); return; } if ($step === 'ask_name') { $this->state->mergeData($userId, ['name' => $event->getText()]); $this->state->setState($userId, 'ask_email'); OutgoingMessage::create('Ваш email?')->to($userId)->send(); return; } if ($step === 'ask_email') { $data = $this->state->getData($userId); // $data['name'] и $event->getText() — готово $this->state->clearState($userId); OutgoingMessage::create('Регистрация завершена!')->to($userId)->send(); } } }
Методы StateManager
$state->getState(int $userId): ?string $state->setState(int $userId, string $state, int $ttl = 3600): void $state->clearState(int $userId): void $state->getData(int $userId): array $state->setData(int $userId, array $data, int $ttl = 3600): void $state->mergeData(int $userId, array $data): void
Artisan-команды
| Команда | Описание |
|---|---|
max-bot:webhook:set {url} |
Установить URL вебхука |
max-bot:webhook:info |
Показать текущий вебхук |
max-bot:webhook:remove |
Удалить вебхук |
max-bot:poll |
Запустить long polling |
Полный API-клиент
Все методы доступны через фасад MaxBot:: или через DI MaxBotClientInterface:
use Dizvestnov\LaravelMaxBot\Contracts\MaxBotClientInterface; class MyService { public function __construct(private MaxBotClientInterface $bot) {} }
Бот
MaxBot::getBotInfo(); MaxBot::editBotInfo(['name' => 'Новое имя']);
Сообщения
MaxBot::sendMessage([...]); MaxBot::editMessage([...]); MaxBot::deleteMessage($messageId); MaxBot::getMessage($messageId); MaxBot::getMessages(['chat_id' => $chatId]); MaxBot::answerOnCallback(['callback_id' => $id, 'text' => 'OK']);
Чаты
MaxBot::getChats(); MaxBot::getChat($chatId); MaxBot::editChat($chatId, ['title' => 'Новое название']); MaxBot::deleteChat($chatId); MaxBot::sendAction($chatId, 'typing'); MaxBot::getPinnedMessage($chatId); MaxBot::pinMessage($chatId, ['message_id' => $messageId]); MaxBot::unpinMessage($chatId);
Участники
MaxBot::getMembership($chatId); MaxBot::leaveChat($chatId); MaxBot::getAdmins($chatId); MaxBot::addAdmins($chatId, [$userId]); MaxBot::deleteAdmin($chatId, $userId); MaxBot::getMembers($chatId); MaxBot::addMembers($chatId, [$userId]); MaxBot::deleteMember($chatId, $userId);
Подписки и обновления
MaxBot::getSubscriptions(); MaxBot::subscribe(['url' => 'https://...', 'secret' => '...']); MaxBot::unsubscribe(); MaxBot::getUpdates(['limit' => 10]);
Медиа
MaxBot::getUploadUrl('image'); MaxBot::getVideoDetails($videoToken);
Тестирование
Мокайте MaxBotClientInterface:
use Dizvestnov\LaravelMaxBot\Contracts\MaxBotClientInterface; public function test_bot_replies(): void { $this->mock(MaxBotClientInterface::class, function ($mock) { $mock->shouldReceive('sendMessage') ->once() ->with(\Mockery::on(fn($p) => $p['text'] === 'Привет!')) ->andReturn(['message_id' => 'abc123']); }); // вызвать логику, которая отправляет сообщение... }
Статус проекта
Пакет находится в активной разработке. Это первый публичный релиз.
API может изменяться до выхода стабильных версий 1.0.0 / 3.0.0. Используйте в production с осторожностью и закрепляйте конкретную версию в
composer.json.
Сообщить об ошибке
-
Поищите в существующих issues — возможно, проблема уже известна: github.com/dizvestnov/laravel-max-bot/issues
-
Создайте новый issue, указав:
- версию пакета (
composer show dizvestnov/laravel-max-bot) - версию PHP и Laravel
- минимальный воспроизводимый пример кода
- ожидаемое поведение и что происходит на самом деле
- полный текст ошибки / стектрейс
- версию пакета (
-
Для вопросов используйте Discussions, а не Issues.
Участие в разработке
- Форкните репозиторий и создайте ветку от
main(3.x) или1.x - Напишите или обновите тесты — покрытие обязательно
- Убедитесь, что все проверки проходят локально:
./vendor/bin/phpunit # тесты ./vendor/bin/pint # стиль кода (3.x) ./vendor/bin/php-cs-fixer fix # стиль кода (1.x) ./vendor/bin/phpstan analyse # статический анализ
- Откройте Pull Request с описанием изменений
Ветка 2.x намеренно пропущена — PR приветствуются.