Перейти к содержимому

Машина состояний (FSM / Диалоги)

Часто боту нужно запросить у пользователя несколько данных последовательно: имя, возраст, город и фото. Реализовывать это через бесконечные if/else и проверки базы данных неудобно.

В ZenithGram встроена поддержка FSM (Finite State Machine), которая позволяет легко создавать линейные диалоги.

Подготовка

Для работы с состояниями необходимо подключить Хранилище (Storage). Библиотека поставляется с встроенным FileStorage (хранение в JSON-файлах), но вы можете реализовать свой интерфейс для Redis или базы данных.

php
<?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: Вход в диалог

Создаем команду, которая инициирует процесс.

php
$bot->onBotCommand('reg', '/reg')
    ->func(function(ZG $tg) {
        $tg->msg("Давайте знакомиться! Как вас зовут?")->send();
        // Переводим пользователя в состояние ожидания имени
        $tg->step('wait_name');
    });

Шаг 2: Обработка Имени

Создаем обработчик для состояния wait_name. Он перехватит любое текстовое сообщение пользователя.

php
$bot->onState('wait_name')
    ->func(function(ZG $tg, string $name) {
        // Сохраняем имя в сессию
        $tg->session(['name' => $name]);
        
        $tg->msg("Приятно познакомиться, {$name}. Сколько вам лет?")->send();
        // Переходим к следующему шагу
        $tg->step('wait_age');
    });

Шаг 3: Обработка Возраста (с валидацией)

Здесь мы проверим, ввел ли пользователь число.

php
$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: Финал

Получаем город, выводим итог и завершаем диалог.

php
$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();
    });

Важные моменты

  1. Приоритет: Обработчик onState имеет приоритет над обычными onText и onCommand. Если пользователь находится в состоянии, бот будет искать соответствующий onState в первую очередь.
  2. Типы обновлений: onState перехватывает как текст, так и нажатия кнопок (callback_query), если пользователь находится в заданном состоянии.
  3. Очистка: Всегда вызывайте endStep(), когда диалог логически завершен, иначе пользователь "застрянет" в последнем состоянии.

Добавьте этот пример в конец файла или в соответствующий раздел.

Опубликовано под лицензией MIT.