Telegram-бот за вечір: що ламається на другий день
Бота для заявок справді можна зробити за вечір. А потім хтось пише HTML у поле імені. Сім речей, які я закладаю в Bot Kit з першого дня.
«Нам потрібен простенький бот: меню, кілька питань і заявка менеджеру. За вечір зробите?» Так, за вечір зроблю . Бот запрацює, клієнт буде щасливий… до другого дня.
Ось що я закладаю в Bot Kit одразу, щоб другого дня не було.
1. Форма — це машина станів, а не купа if-ів
Кожне питання — окремий стан: name → contact → task → confirm. Користувач може зникнути на будь-якому кроці, повернутися через годину чи натиснути /start посеред форми. З машиною станів (у aiogram це StatesGroup) бот завжди знає, що чекає, і не приймає номер телефону за ім’я.
2. Стан — у Redis, а не в пам’яті
Типове сховище — пам’ять процесу. Перезапуск, оновлення, падіння — і всі недописані форми зникли. Redis живе окремо від бота:
def storage() -> BaseStorage:
url = os.getenv("REDIS_URL")
return RedisStorage.from_url(url) if url else MemoryStorage()
3. Введене користувачем — екранувати завжди
Бот надсилає заявку адміну з розміткою HTML (жирні підписи — це ж гарно). Але якщо просто вставити текст користувача, то <b> у полі імені стане розміткою, а незакритий тег Telegram відхилить — і заявка не дійде взагалі.
def summary(data: dict) -> str:
esc = lambda k: html.escape(str(data.get(k, "—"))) # ввід не стає розміткою
return f"<b>Ім'я:</b> {esc('name')}\n<b>Контакт:</b> {esc('contact')}\n<b>Задача:</b> {esc('task')}"
4. Обмежити довжину
Хтось обов’язково вставить у поле «Задача» весь договір. Повідомлення в Telegram обмежене 4096 символами, тож я ріжу кожне поле заздалегідь: ім’я — до 100 символів, задачу — до 1500.
5. Токен — лише в змінних середовища
BOT_TOKEN і ADMIN_ID беруться зі змінних, у коді їх немає. Якщо токен засвітився (скинули в чат, закомітили), його перевипускають у @BotFather за хвилину, і код не треба чіпати. Так, я теж колись отримував токен у чаті .
6. Тест без мережі
Бота можна перевірити без Telegram: підміняєш мережевий шар і «проганяєш» розмову оновленнями.
class FakeSession(BaseSession):
async def make_request(self, bot, method, timeout=None):
sent.append((type(method).__name__, getattr(method, "chat_id", None), getattr(method, "text", None)))
return Message.model_validate({...}) # відповідь «від Telegram»
for up in [msg("/start"), cb("lead"), msg("Оля"), msg("@olya"), msg("Потрібен сайт"), cb("send")]:
await dp.feed_update(bot, up)
assert any(chat == ADMIN_ID for _, chat, _ in sent) # заявка дійшла адміну
Десять секунд, і ти знаєш, що вся форма від /start до «Дякуємо!» працює.
7. Логи й автоперезапуск
Бот має жити на сервері, де його перезапустять після падіння, а логи можна почитати. Тоді на «чому не прийшла заявка?» у тебе є відповідь, а не здогадки. У bebb Cloud це «Логи» з пошуком і сповіщення в Telegram, якщо бот падає по колу.
Підсумок
Бот за вечір — реальність. Бот, який не підведе на другий день, — це ще кілька годин на стани, екранування й тести. Ці години в Bot Kit уже вкладено, тож беріть і адаптуйте .