Примеры решений для Python бота
Приветствую! Здесь вы наверняка найдете, что ищете. Примеры в лаборатории рассчитаны на то, что мы разбираем что-то конкретное.
Текущая статья посвящена bot, диалоги, БД, безопасность и деплой..
Поэтому за теорией по текущей теме вам — в энциклопедию. Если ещё не погружались, то маршрут прост:
- Основы
- Система и сеть
- Данные и разметка
- Код и разработка
- Языки
- Искусственный интеллект
- Проект
- Инфраструктура и безопасность
- Спин-офф
Обязательно пройдитесь.
А теперь приступим к нашему предмету.
Теория и соседние материалы
Секреты
Токен бота только в переменных окружения (BOT_TOKEN). Никогда в git.
Архитектура проекта
bot/
main.py # точка входа
settings.py # конфиг из env
handlers/
start.py
survey.py
services/
user_service.py
repositories/
user_repo.py
| Слой | Ответственность |
|---|---|
| handlers | Команды, кнопки, текст пользователя |
| services | Бизнес-правила |
| repositories | SQL/ORM, Redis |
Шаг 1. Минимальный запуск (PTB v20+)
Проверка: бот отвечает на /start в Telegram.
Шаг 2. Диалог с валидацией (ConversationHandler)
- Пользователь вводит
/survey. - Бот спрашивает имя → проверяет длину.
- Спрашивает возраст → проверяет число.
- Сохраняет в БД и отправляет итог.
async def ask_age(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
text = (update.message.text or "").strip()
if not text.isdigit() or not (1 <= int(text) <= 120):
await update.message.reply_text("Введите возраст числом от 1 до 120")
return ASK_AGE # константа состояния из ConversationHandler
context.user_data["age"] = int(text)
await update.message.reply_text("Спасибо, данные сохранены")
return ConversationHandler.END # завершение диалога
Шаг 3. Inline-кнопки
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
keyboard = [[InlineKeyboardButton("Да", callback_data="yes"),
InlineKeyboardButton("Нет", callback_data="no")]]
await update.message.reply_text("Продолжить?", reply_markup=InlineKeyboardMarkup(keyboard))
Шаг 4. Интеграция с API
import httpx
async def fetch_rate() -> float:
async with httpx.AsyncClient(timeout=10.0) as client:
r = await client.get("https://api.example.com/rate")
r.raise_for_status()
return r.json()["value"]
Добавьте retry и кэш для внешних сервисов.
Шаг 5. База данных (asyncpg)
async with pool.acquire() as conn:
await conn.execute(
"INSERT INTO users (tg_id, name) VALUES ($1, $2) ON CONFLICT (tg_id) DO UPDATE SET name = $2",
tg_id, name,
)
Безопасность и эксплуатация
- Rate limit на тяжёлые команды (Redis + счётчик).
- Валидация и экранирование пользовательского ввода.
- Webhook только по HTTPS с проверкой secret token.
- Health endpoint и метрики (Prometheus) при деплое в Docker.
- Логи в JSON, correlation id на запрос.
Деплой
- Docker-образ с
python:3.12-slim. - Переменные
BOT_TOKEN,DATABASE_URL. - Webhook:
application.run_webhook(..)за reverse proxy. - Systemd или Kubernetes для перезапуска при падении.
Чек-лист готовности
- Бот переживает рестарт без потери критичных данных.
- Ошибки не роняют процесс (глобальный error handler).
- Админ-команды защищены списком
ADMIN_IDS. - Есть тесты на handlers (pytest + моки Update).