aiogram 3 и FSM: три грабли в заказных Telegram-ботах, 2026
PrimeCoder · 2026 · формат: tech
Разбор новинки простыми словами: что это, кому нужно и брать ли.
Показывает, что боевой шаблон бота — это Redis-storage с TTL, отдельный роутер для служебных команд и fallback-хендлеры, иначе заявки теряются при деплое и при «не том» сообщении.
Связанные услуги: каталог, AI Boost Team, стать клиентом.
Заголовок технический, счёт приходит бизнесу
На Хабре в 2026 году вышла заметка разработчика под ником whysafe49: три ошибки в FSM на aiogram 3, собранные на заказных ботах-анкетах. 1.8K охвата, 4 минуты чтения, формат «вечерний разбор». Ценность в другом. Автор не теоретизирует, а переписал свой рабочий шаблон после жалоб заказчиков, и каждая строчка в новом шаблоне выросла из конкретного инцидента.
Почему это стоит внимания собственника. Бот-анкета («нажмите кнопку, ответьте на пять вопросов, заявка уйдёт менеджеру») — типовой заказ за небольшие деньги. Ломается он тоже типово и тихо. Заявка не доходит. В CRM появляется клиент по имени «/cancel». Человек пишет в чат и не получает ответа. Ничего не падает, никто не звонит с пожаром. Просто лиды расходятся по дороге.
Что такое FSM, если вы не инженер
FSM (finite state machine) — память бота о том, на каком шаге сценария находится клиент. Спросили имя — ждём имя. Спросили телефон — ждём телефон. Пока сценарий идёт, бот держит это знание где-то вне диалога: в оперативной памяти процесса, в Redis, в базе.
Где именно — вопрос на деньги, а не на вкус. От этого зависит, что произойдёт при перезапуске контейнера, при добавлении второго бота на тот же сервер и при клиенте, который вернулся к анкете через неделю.
Три грабли, каждая — потерянная заявка
Первая: MemoryStorage в продакшене. Туториалы почти всегда показывают хранилище в памяти — так быстрее запуститься. В бою это значит, что состояние живёт только до перезапуска. Автор выкатил правку текста, контейнер поднялся за пару секунд, а через час заказчик сообщил: клиент ввёл телефон, бот молчит. Анкету человек начал до деплоя, закончил после. Бот уже не помнил, чего ждёт.
MemoryStorage держит состояния в оперативке процесса. Перезапустил бота, и все, кто был посреди анкеты, оказались в пустоте.
Лечится переездом в Redis с ключом, включающим bot_id, и TTL на state и data — в шаблоне автора это сутки. Bot_id нужен, когда на одном Redis крутятся основной и тестовый боты одного заказчика: без префикса они делят состояния одного пользователя. TTL — чтобы брошенные анкеты не копились в базе десятилетиями.
Вторая: /cancel, сохранённый как имя. В aiogram 3 обработчики внутри роутера проверяются в порядке регистрации. Хендлер шага «жду имя» срабатывает на любой текст, а команда — тоже текст. До обработчика /cancel очередь не доходит.
Пользователь на шаге с именем пишет /cancel, и в заявку менеджеру приходит человек по имени «/cancel».
Разница с двойкой принципиальная: там хендлер без фильтра состояния по умолчанию работал только вне состояний, в тройке он ловит любое. Значит, порядок подключения роутеров решает, что попадёт в заявку. Служебные команды выносятся в отдельный роутер и подключаются первыми, плюс страховка в виде отсечения сообщений, начинающихся со слэша.
Третья: бот молчит, когда прислали «не то». На шаге «Телефон для связи?» человек жмёт кнопку контакта, отправляет фото или голосовое. Хендлер ждёт текст, ничего не совпадает, апдейт уходит в никуда. Снаружи это выглядит как зависший бот, и человек закрывает чат.
Что изменилось в шаблоне
| Грабля | Как проявляется | Что чинит |
|---|---|---|
| MemoryStorage в проде | После деплоя клиент посреди анкеты не получает ответа | RedisStorage с with_bot_id=True и TTL сутки |
| Порядок хендлеров | Команда уходит в заявку как ответ на вопрос | Служебный роутер подключается первым |
| Нет ловушки на «не то» | Бот замолкает на фото, голосовом, контакте | Fallback в каждом состоянии плюс общий роутер последним |
Четвёртый пункт автор упоминает вскользь, а он держит всю конструкцию: последний роутер отвечает на всё, что не попало никуда. Он же выручает после истечения TTL — человек вернулся через двое суток, состояния уже нет, но бот хотя бы предлагает начать заново, а не молчит в ответ.
Ничего из этого не сложное. Но каждый пункт появился после конкретной жалобы от живого заказчика, а не из чтения документации.
Кому нужно и что спросить у подрядчика
Всем, у кого бот-анкета собирает заявки, и особенно тем, кто держит бота на VPS с регулярными обновлениями. Разница между «работает на моей машине» и «работает у клиента» стоит примерно как один потерянный лид в неделю — считайте по своему CPL.
Пять вопросов на приёмке, которые закрывают тему:
- Где хранятся состояния FSM: память процесса, Redis или база?
- Что произойдёт с клиентом, который заполняет анкету в момент деплоя?
- Включён ли bot_id в ключ состояния, если на сервере больше одного бота?
- Стоит ли TTL на state и data и какой?
- Что ответит бот на фото, голосовое и случайный текст вне сценария?
Где подвох
Redis — ещё один сервис в контуре. Контейнер в docker compose, вопрос бэкапов, вопрос мониторинга. Для бота на 50 заявок в месяц это лишняя железка, и тут честнее держать состояние в базе проекта, если она уже есть.
TTL сутки — компромисс, а не истина. Клиент, который вернулся на третий день, начнёт анкету заново. Общий fallback спасает от тишины, но не от потери введённых данных.
И главное: шаблон лечит симптом — молчание. Вопрос, который автор задаёт в конце, остаётся открытым: хранить промежуточные данные в FSM или сразу писать в базу. Для длинных сценариев с загрузкой файлов и согласований второе обычно надёжнее. Замеров и цифр по конверсии в разборе нет, обещать рост заявок после переезда на Redis нельзя.
Брать или подождать
Бот в продакшене на MemoryStorage — брать сразу, работы на несколько часов. Бот в разработке — вписать пункты в приёмку до запуска. Бот на 20 заявок в месяц без обновлений и без второго бота на сервере — можно подождать, но вопрос про деплой всё равно стоит держать на виду.
Вердикт
Разбор прикладной: три ошибки, три фикса, никакой архитектурной философии. Ценность в том, что шаблон собран из боли заказчиков, а не из документации, и это ровно тот класс багов, который не видно в демо. Покупая бота, вы покупаете не сценарий, а инфраструктуру вокруг него. Если подрядчик на вопрос «где хранятся состояния» отвечает «а что такое состояния», разговор можно заканчивать.
FAQ
У меня бот на aiogram 2, меня это касается? Поведение хендлеров там другое, но MemoryStorage в проде, отсутствие ловушек на нетекстовые сообщения и молчание после перезапуска — те же грабли.
Сколько это стоит? Оценок в источнике нет. По описанию — часы, не недели, но точную смету без осмотра кода никто честно не назовёт.
Redis обязателен? Нет. Обязательно одно: состояние не должно жить в памяти процесса, который вы перезапускаете при каждом обновлении.
Клиент вернулся через сутки, что будет? Состояния уже нет. Общий fallback ответит и предложит начать заново — это лучше тишины, но данные предыдущей попытки потеряны.
Что сделать на этой неделе
Откройте своего бота, начните анкету, остановите контейнер, поднимите обратно и допишите ответ. Если бот промолчал — вы только что нашли дыру, через которую уходят заявки. Дальше — пять вопросов подрядчику выше.