← Назад к вопросам

Зачем нужен FSM в aiogram?

2.0 Middle🔥 131 комментариев
#Другое

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

FSM (Finite State Machine) в aiogram — управление диалогом

FSM (Конечный автомат) — это инструмент для управления состоянием диалога с пользователем в Telegram боте. Это очень важная часть разработки интерактивных ботов.

Что такое FSM

FSM — это система, которая определяет, в каком "состоянии" находится пользователь и какие действия он может выполнять.

Пример из реальной жизни: заказ доставки

Пользователь может находиться в состояниях:
- start → ожидание действия
- choosing_item → выбирает товар
- entering_address → вводит адрес доставки
- confirming_order → подтверждает заказ
- completed → заказ завершен

Как это помогает

Без FSM:

# ❌ Невозможно отследить, что делает пользователь
@router.message()
async def any_message(message: Message):
    text = message.text
    if text == "Купить":
        # Но как узнать, что это первый раз?
        # Может быть, пользователь уже выбрал товар?
        pass

С FSM:

# ✅ Четко определено состояние пользователя
class OrderStates(StatesGroup):
    choosing_item = State()
    entering_address = State()
    confirming_order = State()

@router.message(OrderStates.choosing_item)
async def process_item_choice(message: Message, state: FSMContext):
    # Здесь точно знаем, что пользователь выбирает товар
    await state.set_state(OrderStates.entering_address)

Основные компоненты

1. StatesGroup — определение состояний

from aiogram.fsm.state import State, StatesGroup

class RegistrationStates(StatesGroup):
    waiting_name = State()
    waiting_email = State()
    waiting_phone = State()
    waiting_password = State()

# Состояния — это просто строки
print(RegistrationStates.waiting_name)  # 'RegistrationStates:waiting_name'

2. FSMContext — работа с состоянием

# Установить состояние
await state.set_state(RegistrationStates.waiting_name)

# Получить текущее состояние
current_state = await state.get_state()
print(current_state)  # 'RegistrationStates:waiting_name'

# Сохранить данные (key-value)
await state.update_data(name="John")

# Получить данные
data = await state.get_data()
print(data)  # {'name': 'John'}

# Очистить состояние
await state.clear()

3. Handlers фильтруют по состоянию

@router.message(RegistrationStates.waiting_name)
async def process_name(message: Message, state: FSMContext):
    # Этот обработчик запустится ТОЛЬКО если пользователь в состоянии waiting_name
    await state.update_data(name=message.text)
    await state.set_state(RegistrationStates.waiting_email)
    await message.answer("Введи email")

Полный пример: регистрация пользователя

from aiogram import Router, F
from aiogram.types import Message
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.filters import Command

router = Router()

# 1. Определяем состояния
class RegistrationStates(StatesGroup):
    waiting_name = State()
    waiting_email = State()
    waiting_phone = State()

# 2. Команда /start — начинаем регистрацию
@router.message(Command("start"))
async def start_registration(message: Message, state: FSMContext):
    await state.set_state(RegistrationStates.waiting_name)
    await message.answer("Как тебя зовут?")

# 3. Получение имени
@router.message(RegistrationStates.waiting_name)
async def process_name(message: Message, state: FSMContext):
    # Проверка
    if len(message.text) < 2:
        await message.answer("Имя слишком короткое, введи еще раз")
        return
    
    # Сохраняем
    await state.update_data(name=message.text)
    await state.set_state(RegistrationStates.waiting_email)
    await message.answer("Введи email")

# 4. Получение email
@router.message(RegistrationStates.waiting_email)
async def process_email(message: Message, state: FSMContext):
    email = message.text
    
    # Валидация
    if "@" not in email:
        await message.answer("Некорректный email, попробуй еще")
        return
    
    await state.update_data(email=email)
    await state.set_state(RegistrationStates.waiting_phone)
    await message.answer("Введи номер телефона")

# 5. Получение телефона — завершение
@router.message(RegistrationStates.waiting_phone)
async def process_phone(message: Message, state: FSMContext):
    # Получаем ВСЕ собранные данные
    data = await state.get_data()
    
    # Сохраняем в БД
    user_data = {
        "name": data["name"],
        "email": data["email"],
        "phone": message.text
    }
    
    # Очищаем состояние
    await state.clear()
    
    await message.answer(
        f"Спасибо! Ты зарегистрирован:\n"
        f"Имя: {user_data['name']}\n"
        f"Email: {user_data['email']}\n"
        f"Телефон: {user_data['phone']}"
    )

Проблема отключения обработчиков

Без FSM пользователь может отправить любое сообщение и оно обработается первым подходящим хендлером.

# ❌ БЕЗ FSM — конфликты обработчиков
@router.message()
async def process_any(message: Message):
    if message.text == "1":
        await message.answer("Вы выбрали 1")
    elif message.text == "2":
        await message.answer("Вы выбрали 2")

# А что если пользователь должен выбирать только ДО регистрации?
# Нет способа это проверить!

# ✅ С FSM — точно контролируем
class MenuStates(StatesGroup):
    selecting_option = State()

@router.message(MenuStates.selecting_option, F.text.in_(["1", "2"]))
async def process_choice(message: Message):
    # Срабатывает ТОЛЬКО если пользователь в selecting_option
    # И текст "1" или "2"
    pass

Сохранение данных между состояниями

class OrderStates(StatesGroup):
    choosing_item = State()
    choosing_quantity = State()
    confirming = State()

@router.message(OrderStates.choosing_item)
async def choose_item(message: Message, state: FSMContext):
    # Сохраняем выбранный товар
    await state.update_data(item=message.text)
    await state.set_state(OrderStates.choosing_quantity)
    await message.answer("Сколько штук?")

@router.message(OrderStates.choosing_quantity)
async def choose_quantity(message: Message, state: FSMContext):
    # Получаем сохраненный товар
    data = await state.get_data()
    item = data["item"]
    quantity = int(message.text)
    
    # Обновляем данные
    await state.update_data(quantity=quantity)
    await state.set_state(OrderStates.confirming)
    
    await message.answer(
        f"Вы заказали {quantity} шт. товара '{item}'. Подтвердить?"
    )

@router.message(OrderStates.confirming, F.text.in_(["Да", "Нет"]))
async def confirm_order(message: Message, state: FSMContext):
    data = await state.get_data()
    
    if message.text == "Да":
        # Сохраняем заказ в БД
        print(f"Заказ: {data}")
    
    await state.clear()  # Очищаем состояние
    await message.answer("Спасибо за заказ!")

Использование с inline кнопками

from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
from aiogram.fsm.state import State, StatesGroup

class PurchaseStates(StatesGroup):
    selecting_item = State()
    confirming = State()

@router.message(Command("shop"))
async def shop(message: Message, state: FSMContext):
    await state.set_state(PurchaseStates.selecting_item)
    
    keyboard = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="Товар 1 ($10)", callback_data="item_1")],
        [InlineKeyboardButton(text="Товар 2 ($20)", callback_data="item_2")],
        [InlineKeyboardButton(text="Отмена", callback_data="cancel")],
    ])
    
    await message.answer("Выбери товар:", reply_markup=keyboard)

@router.callback_query(PurchaseStates.selecting_item)
async def process_item_selection(query, state: FSMContext):
    if query.data == "cancel":
        await state.clear()
        await query.message.edit_text("Отмена")
        return
    
    await state.update_data(item=query.data)
    await state.set_state(PurchaseStates.confirming)
    await query.message.edit_text("Подтвердить заказ?")

Отмена операции (важно!)

# Пользователь может нажать /cancel в любой момент
@router.message(Command("cancel"))
async def cancel_operation(message: Message, state: FSMContext):
    current_state = await state.get_state()
    
    if current_state is None:
        await message.answer("Нечего отменять")
        return
    
    await state.clear()
    await message.answer("Операция отменена")

Хранение FSM

По умолчанию FSM хранится в памяти (MemoryStorage). Для production используй другие хранилища:

# ❌ В памяти (теряется при перезагрузке бота)
from aiogram.fsm.storage.memory import MemoryStorage
storage = MemoryStorage()

# ✅ Redis (рекомендуется)
from aiogram.fsm.storage.redis import RedisStorage
storage = RedisStorage.from_url("redis://localhost:6379/1")

# ✅ PostgreSQL
from aiogram.fsm.storage.sql import SQLAlchemyStorage
from sqlalchemy.ext.asyncio import create_async_engine

engine = create_async_engine("postgresql+asyncpg://...")
storage = SQLAlchemyStorage(engine=engine)

bot = Bot(token=TOKEN, session=session)
dp = Dispatcher(storage=storage)

Когда использовать FSM

  • Многошаговые диалоги (регистрация, заказ, опрос)
  • Валидация пользовательского ввода
  • Контроль потока диалога
  • Сохранение данных между сообщениями

Когда НЕ нужен FSM

  • Простые боты с одной командой
  • Боты с inline кнопками (можно без состояния)
  • Статические ответы

Итог

FSM в aiogram — это мощный инструмент для управления диалогом. Без него очень сложно создавать интерактивные боты, где пользователь проходит несколько шагов.

В production ботах, которые я разрабатывал, FSM использовался во всех сложных сценариях взаимодействия с пользователем. Это делает код чище и логику понятнее.

Зачем нужен FSM в aiogram? | PrepBro