Машина состояний (FSM / Диалоги)
Часто боту нужно запросить у пользователя несколько данных последовательно: имя, возраст, город и фото. Реализовывать это через бесконечные if/else и проверки базы данных неудобно.
В ZenithGram встроена поддержка FSM (Finite State Machine), которая позволяет легко создавать линейные диалоги.
Подготовка
Для работы с состояниями необходимо подключить Хранилище (Storage). Библиотека поставляется с встроенным FileStorage (хранение в JSON-файлах), но вы можете реализовать свой интерфейс для Redis или базы данных.
<?php
require_once __DIR__ . '/vendor/autoload.php';
use ZenithGram\ZenithGram\ZG;
use ZenithGram\ZenithGram\Bot;
use ZenithGram\ZenithGram\Storage\FileStorage;
$tg = ZG::create(BOT_TOKEN);
$bot = new Bot($tg);
// Инициализируем хранилище в Bot
$bot->setStorage(new FileStorage());
// Или напрямую в ZG
$tg->setStorage(new FileStorage());Основные методы
$tg->step('название_шага')— Переводит пользователя в указанное состояние.$bot->onState('название_шага')— Обработчик, который сработает, только если пользователь находится в этом состоянии.$tg->session(['ключ' => 'значение'])— Сохраняет данные в сессию пользователя.$tg->session()— Возвращает массив всех сохраненных данных.$tg->endStep()— Сбрасывает состояние и очищает данные сессии.
Пример: Анкета регистрации
Пример: создадим бота, который спрашивает Имя, Возраст и Город.
Шаг 1: Вход в диалог
Создаем команду, которая инициирует процесс.
$bot->onBotCommand('reg', '/reg')
->func(function(ZG $tg) {
$tg->msg("Давайте знакомиться! Как вас зовут?")->send();
// Переводим пользователя в состояние ожидания имени
$tg->step('wait_name');
});Шаг 2: Обработка Имени
Создаем обработчик для состояния wait_name. Он перехватит любое текстовое сообщение пользователя.
$bot->onState('wait_name')
->func(function(ZG $tg, string $name) {
// Сохраняем имя в сессию
$tg->session(['name' => $name]);
$tg->msg("Приятно познакомиться, {$name}. Сколько вам лет?")->send();
// Переходим к следующему шагу
$tg->step('wait_age');
});Шаг 3: Обработка Возраста (с валидацией)
Здесь мы проверим, ввел ли пользователь число.
$bot->onState('wait_age')
->func(function(ZG $tg, string $age) {
// Простая валидация
if (!is_numeric($age)) {
$tg->msg("Пожалуйста, введите возраст цифрами.")->send();
return; // Не меняем шаг, пользователь останется в wait_age
}
$tg->session(['age' => $age]);
$tg->msg("Отлично! Из какого вы города?")->send();
$tg->step('wait_city');
});Шаг 4: Финал
Получаем город, выводим итог и завершаем диалог.
$bot->onState('wait_city')
->func(function(ZG $tg, string $city) {
// Сохраняем последнее значение
$tg->session(['city' => $city]);
// Получаем все накопленные данные
$data = $tg->session();
$tg->msg(
"🎉 Регистрация завершена!\n\n" .
"👤 Имя: {$data['name']}\n" .
"🎂 Возраст: {$data['age']}\n" .
"🏙 Город: {$data['city']}"
)->send();
// Сбрасываем состояние и очищаем данные
$tg->endStep();
});Важные моменты
- Приоритет: Обработчик
onStateимеет приоритет над обычнымиonTextиonCommand. Если пользователь находится в состоянии, бот будет искать соответствующийonStateв первую очередь. - Типы обновлений:
onStateперехватывает как текст, так и нажатия кнопок (callback_query), если пользователь находится в заданном состоянии. - Очистка: Всегда вызывайте
endStep(), когда диалог логически завершен, иначе пользователь "застрянет" в последнем состоянии.
Добавьте этот пример в конец файла или в соответствующий раздел.