Всем привет!

Если на работе я — инженер-проектировщик, то дома я — садовод-огородник, и при большом количестве разных зеленых друзей на подоконнике — теряешься в расписании полива. Поэтому я решила написать бота для растений — мы напишем простого, но полезного помощника: Telegram-бота, который будет напоминать, когда поливать цветы.

В этой части мы реализуем диалоговую логику. А во второй части — подключим базу данных и настроим уведомления.

К концу первой части у вас будет бот, который:

  • здоровается по имени

  • принимает команду /add_plant

  • ведёт диалог: «Как зовут растение? Когда поливал(а)? Через сколько дней снова?»

Что нам понадобится?

Что

Для чего

Telegram

Где будет жить бот

Python 3.12+

Язык программирования

Любой текстовый редактор (Блокнот, VS Code)

Чтобы писать код

Шаг 1. Создаём бота через — и делаем его по-настоящему своим

Telegram → @BotFather → Start → Список команд

Создаём бота через
Создаём бота через @BotFather

Нажимаем /newbot. Выбираем имя и username.

Важно про username!

  • Должен оканчиваться на bot (можно _bot, Bot, BOT — регистр не важен)

  • Только латинские буквы, цифры и подчёркивания

  • Максимум 32 символа

  • Должен быть уникальным во всём Telegram

Создаём бота через
Создаём бота через @BotFather

Получаем токен и прячем его. (Токен мы никому не передаем, не выкладываем на Git (вообще не выкладываем в интернет)).

Если всё прошло успешно, BotFather отправит:

Успешное создание бота. Получение токена
Успешное создание бота. Получение токена

Опционально, но очень рекомендуется: настраиваем «личность» бота
После создания бота BotFather предложит команды для настройки. Я хочу чтобы бот выглядел красиво и был настоящим помощником.

Команды для  настройки бота
Команды для настройки бота

Я же заполнила описание, поставила аватарку. Теперь вот выглядит миленько.

Переходим в наш бот, нажимаем /start и видим, что бот неактивен, а теперь займемся его оживлением.

Шаг 2. Устанавливаем Python (можно пропустить, если он уже установлен)

Переходим в cmd и вводим python --version. Если выдает ошибку - то нам нужно сначала скачать и установить Python себе на компьютер.
Выбираем актуальную версию с официального сайта https://www.python.org/downloads/windows/ , скачиваем и устанавливаем.

ВАЖНО! На первом экране установки поставьте галочку:
✅ Add Python to PATH (Это позволит запускать python из командной строки)

Возвращаемся в cmd вводим python --version и должны получить версию (может быть другая)

Версия Python на моем АРМ.
Версия Python на моем АРМ.

И надо не забыть установить библиотеку python-telegram-bot
pip install python-telegram-bot
Команда должна (и обязана) совершиться без ошибки.

Шаг 3. Пишем простой код для бота

As for me, самая тяжелая часть.

Создаем папку проекта, где будут храниться файлы нашего бота. Создать внутри папки файл, например, bot.py. В файле bot.py пишем код:

Подробнее про использование используемых библиотек, все четко и по делу - https://docs-python.ru/packages/biblioteka-python-telegram-bot-python/

#Импортируем нужные инструменты из библиотеки python-telegram-bot, в данном случае это Update:
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes

#СЮДА ВСТАВЛЯЕТСЯ ТОКЕН, КОТОРЫЙ ВЫДАЛ 
#никогда никому не показывайте этот токен ! (тот, который ниже - ненастоящий)
BOT_TOKEN = "876543234567898765432"

#функция, которая срабатывает, когда пользователь пишет /start
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
#достаём информацию о пользователе
effective_user — это объект, содержащий: имя, ID, username и т.д. (берём имя из профиля Telegram)
user = update.effective_user
name = user.first_name or "друг"  
#Если имя не указано - то напишем друг)      
#отправляем ответное сообщение прямо в чат с пользователем
await update.message.reply_text(     
f"Привет, {name}! ?\n"              
 "Я — бот для ухода за цветами.\n"    
 "Рада тебя видеть!"               
 ) 

#Функция, которая запускает бота
def main():
app = Application.builder().token(BOT_TOKEN).build()

#говорим боту: «Когда кто-то напишет /start — вызови функцию start»
app.add_handler(CommandHandler("start", start))

#это сообщение выводится в терминале (чисто прикормка для тревожника, 
#чтобы мы знали что код отработал без ошибок и не зациклился)
print("Бот запущен! Напиши ему в Telegram.")

#запускаем цикл - бот сидит и ждет новых сообщений
run_polling()
app.run_polling()

#эта строчка стандартная «заглушка»
if name == "main":
main()

Сохраняем файл, переходим в cmd и вводим
C:\Users\admin\flower_bot>python bot.py

Сработала прикормка для тревожника. Бот запустился.
Сработала прикормка для тревожника. Бот запустился.

Переходим в Telegram и отправляем /start

Шаг 4. Добавляем диалог: «Расскажи мне о своём растении»

Структура кода будет такая:

Пользователь: /add_plant

Бот: → вызывает ask_name (entry_point)

Пользователь: "Фикус"

Бот: сохраняет 'Фикус' → спрашивает дату → переходит в состояние DATE

Пользователь: "15.11.2025"

Бот: сохраняет дату → спрашивает интервал → переходит в INTERVAL

Пользователь: "7"

Бот: выводит сводку → очищает данные → завершает диалог

Нам нужно добавить новые функции, чтобы бот "разговаривал" с нами

Спрашиваем название растения: ask_name

Назначение: Получить и сохранить название растения от пользователя. Вызывается после команды /add_plant.

Что делает:
• Берёт текст сообщения (название растения) из update.message.text.
• Сохраняет его во временное хранилище пользователя context.user_data['name'].
• Отправляет сообщение с запросом даты.
• Возвращает состояние DATE → бот "переключается" на следующий шаг диалога.
async def ask_name(update, context):
context.user_data['name'] = update.message.text # ← записываем в "блокнот" пользователя
await update.message.reply_text("? Дата последнего полива?")
return DATE # → переходим к шагу DATE

Спрашиваем дату последнего полива: ask_date

Назначение: Получить и сохранить дату последнего полива.

Вызывается когда бот находится в состоянии DATE (после ask_name). Что делает:
• Берёт текст сообщения (дату) без валидации
• Сохраняет в context.user_data['date'].
• Запрашивает интервал полива.
• Возвращает состояние INTERVAL → переход к финальному шагу.
async def ask_date(update, context):
context.user_data['date'] = update.message.text ← сохраняем дату как строку
await update.message.reply_text("⏳ Интервал полива (дней)?")
return INTERVAL → переходим к шагу INTERVAL

Спрашиваем интервал полива: ask_interval

Назначение: Получить интервал и завершить диалог, показав сводку.
Вызывается, когда бот состоянии INTERVAL (после ask_date).

Что делает:
• Извлекает все данные из "блокнота": name, date, и текущее сообщение — интервал.
• Формирует и отправляет итоговое сообщение с подтверждением.
• Очищает user_data
• Возвращает ConversationHandler.END — диалог закрывается.
async def ask_interval(update, context):

Берём всё, что накопили:
name = context.user_data['name'] # название
date = context.user_data['date'] # дата
interval = update.message.text # интервал

Подтверждаем добавление:
await update.message.reply_text(
f"✅ {name} добавлено!\n"
f"Последний полив: {date}\n"
f"Интервал: {interval} дней"
)

В итоге у нас получается такой код:

from telegram import Update
from telegram.ext import (
    Application,
    CommandHandler,
    MessageHandler,
    filters,
    ContextTypes,
    ConversationHandler,
)

# ТОКЕН!
BOT_TOKEN = "838765567890-098765456789098765"

# Этапы диалога (состояния ConversationHandler)
# Используем константы для читаемости: 0 → NAME, 1 → DATE, 2 → INTERVAL
NAME, DATE, INTERVAL = range(3)


# === /start — приветствие и инструкция ===
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(
        "Привет! ?\n Чтобы добавить растение — отправь /add_plant"
    )


# === /add_plant — запуск диалога ===
async def add_plant_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """
       Бот переходит в состояние NAME - ожидаем название растения.
    """
    await update.message.reply_text("? Название растения?")
    return NAME  # → переключаемся на этап NAME


# === Этап NAME: получаем и проверяем название ===
async def ask_name(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """
    Обрабатывает ввод названия растения.
    - Проверяем, что строка не пустая (минимальная валидация).
    - Сохраняем в user_data — временный контекст пользователя.
    - Переходим к следующему этапу — DATE.
    """
    plant_name = update.message.text.strip()
    if not plant_name:
        await update.message.reply_text("❌ Введи название.")
        return NAME  # остаёмся на том же этапе (повторный запрос)

    context.user_data['plant_name'] = plant_name  # ← сохраняем в "блокнот"
    await update.message.reply_text("? Дата последнего полива (ДД.ММ.ГГГГ)?")
    return DATE  # → переходим к этапу DATE


# === Этап DATE: парсим дату ===
async def ask_date(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """
    Ожидаем дату в строгом формате ДД.ММ.ГГГГ.
    - Используем datetime.strptime — защита от некорректного ввода.
    - При ошибке — просим повторить, не выходя из состояния.
    - Сохраняем в ISO-формате (YYYY-MM-DD)
    """
    try:
        from datetime import datetime
        # Парсим дату по шаблону — строгая валидация
        last_watered = datetime.strptime(update.message.text.strip(), "%d.%m.%Y").date()
        # Сохраняем как строку в формате ISO (2025-11-19) — стандарт для хранения
        context.user_data['last_watered'] = last_watered.isoformat()
        await update.message.reply_text("⏳ Интервал полива (дней)?")
        return INTERVAL  # → переходим к финальному этапу
    except ValueError:
        # Если формат неверный — не прерываем диалог, а просим исправить
        await update.message.reply_text("❌ Формат: ДД.ММ.ГГГГ")
        return DATE  # остаёмся в состоянии DATE


# === Этап INTERVAL: проверяем и завершаем ===
async def ask_interval(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """
    Получаем интервал полива.
    - Проверяем, что это целое положительное число.
    - Формируем итоговое сообщение.
    - Очищаем user_data — хорошая практика (избегаем "утечек" между сессиями).
    - Завершаем диалог.
    """
    try:
        interval = int(update.message.text.strip())
        if interval <= 0:
            raise ValueError  # чтобы обработать как ошибку ввода

        # Достаём ранее сохранённые данные
        plant_name = context.user_data['plant_name']
        last_watered = context.user_data['last_watered']

        # Подтверждаем добавление (в реальном проекте — здесь вызов БД)
        await update.message.reply_text(
            f"✅ {plant_name} добавлено!\n"
            f"Последний полив: {last_watered}\n"
            f"Интервал: {interval} дней"
        )

        # Чистим временные данные
        context.user_data.clear()
        return ConversationHandler.END  # ← диалог завершён

    except ValueError:
        await update.message.reply_text("❌ Введи целое число > 0.")
        return INTERVAL  # повторный запрос, не выходя из состояния


# === Основная функция: сборка и запуск бота ===
def main():
    """Собирает приложение и регистрирует обработчики."""
    app = Application.builder().token(BOT_TOKEN).build()

    # Диалоговый обработчик: управляет состояниями и переходами
    conv_handler = ConversationHandler(
        # Диалог начинается с команды /add_plant
        entry_points=[CommandHandler("add_plant", add_plant_start)],

        # Что происходит на каждом этапе
        states={
            NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, ask_name)],
            DATE: [MessageHandler(filters.TEXT & ~filters.COMMAND, ask_date)],
            INTERVAL: [MessageHandler(filters.TEXT & ~filters.COMMAND, ask_interval)],
        },
      
        # Диалог изолирован по пользователям (по умолчанию True)
        per_user=True,
    )

    # Регистрируем обработчики команд и диалогов
    app.add_handler(CommandHandler("start", start))
    app.add_handler(conv_handler)

    print("✅ Бот с диалогом запущен!")
    app.run_polling()


if __name__ == "__main__":
    main()

Переходим в Телеграмм и начинаем общаться с нашим ботом

Бот с нами заговорил
Бот с нами заговорил

В этой статье мы собрали «скелет» Telegram-бота: от регистрации в BotFather до рабочего диалогового интерфейса. Реализовали пошаговое добавление растения.

Мы реализовали пошаговое добавление растения через ConversationHandler: бот последовательно запрашивает название, дату последнего полива и интервал, сохраняя промежуточные данные в context.user_data.

Что дальше?

Во второй части мы:

  • подключим базу данных, чтобы данные не исчезали после перезапуска;

  • добавим команду /list — чтобы пользователь видел все свои растения;

  • и настроим напоминания о поливе через JobQueue

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


  1. Kahelman
    23.11.2025 18:19

    „безумие и отвага» не проще/ лучше было бы сделать автоматическую систему полива и забыть о проблеме?