Тема 5 из 6

Диалоги и состояния

Создание напоминания — это диалог: что напомнить? через сколько? Бот должен помнить, на каком шаге пользователь и что он уже ответил. Для этого в aiogram есть машина состояний (FSM).

Состояния

from aiogram.fsm.state import State, StatesGroup


class New(StatesGroup):
    text = State()          # ждём текст напоминания
    minutes = State()       # ждём число минут

FSMContext

Добавьте в обработчик параметр state: FSMContext — aiogram передаст «память» этого пользователя в этом чате:

from aiogram.fsm.context import FSMContext


@router.message(Command("new"))
async def start(message: Message, state: FSMContext):
    await state.set_state(New.text)
    await message.answer("Что напомнить?")


@router.message(New.text)                     # только в состоянии New.text
async def got_text(message: Message, state: FSMContext):
    await state.update_data(text=message.text)
    await state.set_state(New.minutes)
    await message.answer("Через сколько минут?")


@router.message(New.minutes)
async def got_minutes(message: Message, state: FSMContext):
    data = await state.get_data()             # {"text": "…"}
    await state.clear()                       # диалог окончен
    await message.answer(f"Готово: {data['text']}")
  • set_state(…) — перейти на шаг; get_state() — текущий шаг (None вне диалога);
  • update_data(ключ=значение) — запомнить, get_data() — словарь всего запомненного;
  • clear() — забыть и состояние, и данные. Не забывайте его в конце диалога: иначе бот так и будет считать каждое сообщение ответом на последний вопрос.

Неверный ответ

Если ответ не подходит — не меняйте состояние, просто переспросите. Пользователь останется на том же шаге.

Отмена

Пользователь должен иметь возможность выйти из диалога. Обработчик без фильтра состояния работает в любом состоянии, поэтому /cancel регистрируют раньше обработчиков шагов — иначе шаг перехватит «/cancel» как обычный ответ.

Где хранится память

По умолчанию Dispatcher хранит состояния в памяти процесса (MemoryStorage): после перезапуска бота все диалоги забудутся. В настоящих ботах берут RedisStorage — состояния переживают перезапуски.

Итог

  • StatesGroup и State; фильтр по состоянию в декораторе;
  • state.set_state, get_state, update_data, get_data, clear;
  • неверный ответ — переспросить, не меняя шага;
  • /cancel — раньше обработчиков шагов.