СДЕЛАЙТЕ СВОИ УРОКИ ЕЩЁ ЭФФЕКТИВНЕЕ, А ЖИЗНЬ СВОБОДНЕЕ

Благодаря готовым учебным материалам для работы в классе и дистанционно

Скидки до 50 % на комплекты
только до

Готовые ключевые этапы урока всегда будут у вас под рукой

Организационный момент

Проверка знаний

Объяснение материала

Закрепление изученного

Итоги урока

Методическая разработка «Создание Telegram-бота для Юных натуралистов»

Категория: Информатика

Нажмите, чтобы узнать подробности

Просмотр содержимого документа
«Методическая разработка «Создание Telegram-бота для Юных натуралистов»»

МУНИЦИПАЛЬНОЕ АВТОНОМНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ДОПОЛНИТЕЛЬНОГО ОБРАЗОВАНИЯ

«ЦЕНТР ДЕТСКОГО ТЕХНИЧЕСКОГО ТВОРЧЕСТВА»













Методическая разработка

«Создание Telegram-бота для Юных натуралистов»

к дополнительной общеобразовательной

общеразвивающей программе

технической направленности

«Программирование на Python»



Возраст детей: 10-17 лет









Автор: Костычев Вадим Александрович









г. Заречный Пензенской области

2025 г.


Данный методический материал разработан для изучения программирования на Python через создание Telegram-бота для Юных натуралистов. Бот поможет школьникам и педагогам изучить основы работы с растениями, базами данных и асинхронным программированием. Он позволяет добавлять растения, вести их учёт, получать напоминания о поливе и управлять данными через удобный интерфейс Telegram.

Цели:

- освоить основы Python и асинхронного программирования;

- научиться работать с базами данных SQLite;

- понять принципы разработки чат-ботов;

- развить навыки ухода за растениями через цифровые инструменты.


Описание бота

Telegram-бот для Юных натуралистов выполняет следующие функции:

  1. Добавление растений: пользователи могут добавлять растения с названием, описанием, фотографией и интервалом полива.

  2. Глоссарий растений: отображает список всех растений с информацией о том, кто их добавил, и позволяет просмотреть подробности.

  3. Напоминания о поливе: отправляет уведомления в 15:00, если растение требует полива.

  4. Проверка полива: показывает даты следующего полива для растений.

  5. Удаление растений: доступно только администраторам с подтверждением действия.


Бот использует SQLite для хранения данных и Telegram API для взаимодействия с пользователями.


Необходимое программное обеспечение:

  • Python: Версия 3.8 или выше.

  • Библиотека python-telegram-bot: Версия 20.7 с поддержкой job-queue (pip install "python-telegram-bot[job-queue]").

  • SQLite: Встроен в Python (модуль sqlite3).

  • Операционная система: Windows, macOS или Linux.


Подготовка:

  1. Установите Python:

sudo apt install python3 python3-pip # Для Linux


  1. Установите библиотеку:

pip install "python-telegram-bot[job-queue]"


  1. Создайте токен бота через BotFather в Telegram:

  • Отправьте /start и /newbot.

  • Следуйте инструкциям, чтобы получить токен (например, 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11).

Структура кода

Код бота организован следующим образом:

  1. Библиотеки:

    • sqlite3: для работы с базой данных;

    • logging: для логирования событий;

    • datetime, timedelta: для расчёта времени полива;

    • модули telegram и telegram;ext: для взаимодействия с telegram api;

    • os: для работы с файловой системой.

  2. Инициализация базы данных:

Функция init_db() создаёт таблицу plants с полями: id, name, description, photo, user_id, user_name, watering_interval, last_watered, next_watering.


  1. Обработчики:

  • /start: Выводит приветствие и главное меню.

  • Добавление растения: Последовательный ввод данных через состояния (add_plant, handle_message, handle_photo).

  • Глоссарий: Отображение списка растений (glossary, show_plant).

  • Полив: Проверка растений, требующих полива (watering).

  • Проверка дат полива: Показывает расписание полива (check_watering_dates).

  • Удаление: Только для админов с подтверждением (delete_plant, confirm_delete, execute_delete).

  • Уведомления: Отправка напоминаний в 15:00 (check_watering).


Главное меню:

Реализовано через InlineKeyboardMarkup с кнопками для всех функций. Администраторам доступна дополнительная кнопка "Удалить растение".


Ключевые функции


Добавление растения


Процесс:

    • Пользователь нажимает "Добавить растение".

    • Бот запрашивает название, описание, интервал полива и фото.

    • Данные сохраняются в базе через save_plant.


Код:


async def add_plant(update: Update, context: ContextTypes.DEFAULT_TYPE):

query = update.callback_query

await query.answer()

context.user_data["state"] = "add_plant_name"

await query.message.reply_text("Введите название растения:")


Использует context.user_data для хранения временных данных. Фото сохраняется в папку photos.


Глоссарий


Процесс:

    • Показывает список растений с именами добавивших.

    • При выборе растения отображается описание и фото.


Код:


async def glossary(update: Update, context: ContextTypes.DEFAULT_TYPE):

conn = sqlite3.connect("plants.db")

c = conn.cursor()

c.execute("SELECT id, name, user_name FROM plants")

plants = c.fetchall()

conn.close()


Напоминания о поливе


Процесс:

    • Ежедневно в 15:00 бот проверяет растения, где next_watering

    • Отправляет уведомления и обновляет время полива.


Код:


async def check_watering(context: ContextTypes.DEFAULT_TYPE):

c.execute("SELECT user_id, name, watering_interval FROM plants WHERE next_watering

plants = c.fetchall()

for user_id, name, watering_interval in plants:

await context.bot.send_message(chat_id=user_id, text=f"🌱 Напоминание: пора полить растение '{name}'!")


Проверка полива


Процесс: показывает даты следующего полива для растений пользователя или всех (для админов).


Код:


async def check_watering_dates(update: Update, context: ContextTypes.DEFAULT_TYPE):

if is_admin(user.id):

c.execute("SELECT name, next_watering, user_name FROM plants")

else:

c.execute("SELECT name, next_watering, user_name FROM plants WHERE user_id = ?", (user.id,))


Удаление растений

Процесс:

  • Доступно только админам.

  • Требует подтверждения через кнопки "Да, удалить" или "Отмена".


Код:


async def delete_plant(update: Update, context: ContextTypes.DEFAULT_TYPE):

if not is_admin(user.id):

await query.message.reply_text("Только администратор может удалять растения.")

return


Полный листинг программы:


import sqlite3

import logging

from datetime import datetime, timedelta

from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup

from telegram.ext import (

Application,

CommandHandler,

MessageHandler,

filters,

ContextTypes,

CallbackQueryHandler,

JobQueue,

)

import os


# Настройка логирования

logging.basicConfig(format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO)

logger = logging.getLogger(__name__)


# Инициализация базы данных

def init_db():

conn = sqlite3.connect("plants.db")

c = conn.cursor()

c.execute("""

CREATE TABLE IF NOT EXISTS plants (

id INTEGER PRIMARY KEY AUTOINCREMENT,

name TEXT NOT NULL,

description TEXT,

photo TEXT,

user_id INTEGER,

user_name TEXT,

watering_interval INTEGER,

last_watered TIMESTAMP,

next_watering TIMESTAMP

)

""")

conn.commit()

conn.close()


# Проверка, является ли пользователь администратором

def is_admin(user_id):

ADMIN_IDS = [123456789] # Замените на реальные ID администраторов

return user_id in ADMIN_IDS


# Главное меню

def get_main_menu(is_admin=False):

keyboard = [

[InlineKeyboardButton("Добавить растение", callback_data="add_plant")],

[InlineKeyboardButton("Глоссарий растений", callback_data="glossary")],

[InlineKeyboardButton("Полив", callback_data="watering")],

[InlineKeyboardButton("Проверка полива", callback_data="check_watering_dates")],

]

if is_admin:

keyboard.append([InlineKeyboardButton("Удалить растение", callback_data="delete_plant")])

return InlineKeyboardMarkup(keyboard)


# Обработчик команды /start

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):

user = update.effective_user

welcome_text = (

f"Привет, {user.first_name}! Добро пожаловать в бот Юных натуралистов 🌱\n"

"Выбери, что хочешь сделать:"

)

await update.message.reply_text(welcome_text, reply_markup=get_main_menu(is_admin(user.id)))


# Обработчик добавления растения

async def add_plant(update: Update, context: ContextTypes.DEFAULT_TYPE):

query = update.callback_query

await query.answer()

context.user_data["state"] = "add_plant_name"

await query.message.reply_text("Введите название растения:")


# Обработчик текстовых сообщений для добавления растения

async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):

user = update.effective_user

text = update.message.text

state = context.user_data.get("state")


if state == "add_plant_name":

context.user_data["plant_name"] = text

context.user_data["state"] = "add_plant_description"

await update.message.reply_text("Введите описание растения:")

elif state == "add_plant_description":

context.user_data["plant_description"] = text

context.user_data["state"] = "add_plant_interval"

await update.message.reply_text("Укажите интервал полива (в днях, например, 7):")

elif state == "add_plant_interval":

try:

interval = int(text)

context.user_data["watering_interval"] = interval

context.user_data["state"] = "add_plant_photo"

await update.message.reply_text("Отправьте фотографию растения (или напишите 'без фото'):")

except ValueError:

await update.message.reply_text("Пожалуйста, введите число (например, 7):")

elif state == "add_plant_photo" and text.lower() == "без фото":

save_plant(context, user, None)

await update.message.reply_text("Растение добавлено!", reply_markup=get_main_menu(is_admin(user.id)))

context.user_data.clear()

else:

await update.message.reply_text("Пожалуйста, следуйте инструкциям.")


# Обработчик фотографий

async def handle_photo(update: Update, context: ContextTypes.DEFAULT_TYPE):

user = update.effective_user

if context.user_data.get("state") == "add_plant_photo":

photo = update.message.photo[-1]

file = await photo.get_file()

photo_path = f"photos/{photo.file_id}.jpg"

os.makedirs("photos", exist_ok=True)

await file.download_to_drive(photo_path)

save_plant(context, user, photo_path)

await update.message.reply_text("Растение добавлено!", reply_markup=get_main_menu(is_admin(user.id)))

context.user_data.clear()


# Сохранение растения в базе данных

def save_plant(context, user, photo_path):

plant_name = context.user_data["plant_name"]

description = context.user_data["plant_description"]

interval = context.user_data["watering_interval"]

last_watered = datetime.now()

next_watering = last_watered + timedelta(days=interval)


conn = sqlite3.connect("plants.db")

c = conn.cursor()

c.execute(

"""

INSERT INTO plants (name, description, photo, user_id, user_name, watering_interval, last_watered, next_watering)

VALUES (?, ?, ?, ?, ?, ?, ?, ?)

""",

(

plant_name,

description,

photo_path,

user.id,

user.first_name,

interval,

last_watered,

next_watering,

),

)

conn.commit()

conn.close()


# Обработчик глоссария

async def glossary(update: Update, context: ContextTypes.DEFAULT_TYPE):

query = update.callback_query

await query.answer()


conn = sqlite3.connect("plants.db")

c = conn.cursor()

c.execute("SELECT id, name, user_name FROM plants")

plants = c.fetchall()

conn.close()


if not plants:

await query.message.reply_text("Глоссарий пуст. Добавьте растение!")

return


keyboard = [

[InlineKeyboardButton(f"{name} (добавил: {user_name})", callback_data=f"plant_{id}")]

for id, name, user_name in plants

]

keyboard.append([InlineKeyboardButton("Назад", callback_data="back")])

await query.message.reply_text("Выберите растение:", reply_markup=InlineKeyboardMarkup(keyboard))


# Обработчик выбора растения в глоссарии

async def show_plant(update: Update, context: ContextTypes.DEFAULT_TYPE):

query = update.callback_query

await query.answer()

plant_id = int(query.data.split("_")[1])


conn = sqlite3.connect("plants.db")

c = conn.cursor()

c.execute("SELECT name, description, photo, user_name FROM plants WHERE id = ?", (plant_id,))

plant = c.fetchone()

conn.close()


name, description, photo, user_name = plant

text = f"🌱 {name}\nОписание: {description}\nДобавил: {user_name}"

if photo:

await query.message.reply_photo(photo=open(photo, "rb"), caption=text)

else:

await query.message.reply_text(text)

await query.message.reply_text("Выберите действие:", reply_markup=get_main_menu(is_admin(query.from_user.id)))


# Обработчик полива

async def watering(update: Update, context: ContextTypes.DEFAULT_TYPE):

query = update.callback_query

await query.answer()

user = query.from_user

conn = sqlite3.connect("plants.db")

c = conn.cursor()


if is_admin(user.id):

c.execute("SELECT name, next_watering FROM plants WHERE next_watering

else:

c.execute(

"SELECT name, next_watering FROM plants WHERE user_id = ? AND next_watering

(user.id, datetime.now()),

)

plants = c.fetchall()

conn.close()


if not plants:

await query.message.reply_text("Нет растений, которые нужно полить.")

return


text = "Растения, которые нужно полить:\n"

for name, next_watering in plants:

text += f"- {name} (полить сейчас)\n"

await query.message.reply_text(text, reply_markup=get_main_menu(is_admin(user.id)))


# Обработчик проверки дат полива

async def check_watering_dates(update: Update, context: ContextTypes.DEFAULT_TYPE):

query = update.callback_query

await query.answer()

user = query.from_user


conn = sqlite3.connect("plants.db")

c = conn.cursor()


if is_admin(user.id):

c.execute("SELECT name, next_watering, user_name FROM plants")

else:

c.execute(

"SELECT name, next_watering, user_name FROM plants WHERE user_id = ?",

(user.id,)

)

plants = c.fetchall()

conn.close()


if not plants:

await query.message.reply_text("У вас нет растений для проверки.")

return


text = "📅 Даты следующего полива:\n"

for name, next_watering, user_name in plants:

try:

next_watering_date = datetime.strptime(next_watering, "%Y-%m-%d %H:%M:%S.%f")

except ValueError:

next_watering_date = datetime.now()

formatted_date = next_watering_date.strftime("%d.%m.%Y %H:%M")

text += f"- {name} (добавил: {user_name}): {formatted_date}\n"

await query.message.reply_text(text, reply_markup=get_main_menu(is_admin(user.id)))


# Обработчик удаления растения (только для администратора)

async def delete_plant(update: Update, context: ContextTypes.DEFAULT_TYPE):

query = update.callback_query

await query.answer()

user = query.from_user


if not is_admin(user.id):

await query.message.reply_text("Только администратор может удалять растения.")

return


conn = sqlite3.connect("plants.db")

c = conn.cursor()

c.execute("SELECT id, name FROM plants")

plants = c.fetchall()

conn.close()


if not plants:

await query.message.reply_text("Нет растений для удаления.")

return


keyboard = [

[InlineKeyboardButton(name, callback_data=f"confirm_delete_{id}")]

for id, name in plants

]

keyboard.append([InlineKeyboardButton("Назад", callback_data="back")])

await query.message.reply_text("Выберите растение для удаления:", reply_markup=InlineKeyboardMarkup(keyboard))


# Подтверждение удаления

async def confirm_delete(update: Update, context: ContextTypes.DEFAULT_TYPE):

query = update.callback_query

await query.answer()

plant_id = int(query.data.split("_")[2])


conn = sqlite3.connect("plants.db")

c = conn.cursor()

c.execute("SELECT name FROM plants WHERE id = ?", (plant_id,))

plant_name = c.fetchone()[0]

conn.close()


keyboard = [

[InlineKeyboardButton("Да, удалить", callback_data=f"delete_{plant_id}")],

[InlineKeyboardButton("Отмена", callback_data="back")],

]

await query.message.reply_text(

f"Вы уверены, что хотите удалить растение '{plant_name}'?",

reply_markup=InlineKeyboardMarkup(keyboard)

)


# Окончательное удаление

async def execute_delete(update: Update, context: ContextTypes.DEFAULT_TYPE):

query = update.callback_query

await query.answer()

plant_id = int(query.data.split("_")[1])


conn = sqlite3.connect("plants.db")

c = conn.cursor()

c.execute("DELETE FROM plants WHERE id = ?", (plant_id,))

conn.commit()

conn.close()


await query.message.reply_text("Растение удалено!", reply_markup=get_main_menu(is_admin(query.from_user.id)))


# Обработчик кнопки "Назад"

async def back(update: Update, context: ContextTypes.DEFAULT_TYPE):

query = update.callback_query

await query.answer()

await query.message.reply_text("Главное меню:", reply_markup=get_main_menu(is_admin(query.from_user.id)))


# Функция для отправки уведомлений о поливе

async def check_watering(context: ContextTypes.DEFAULT_TYPE):

conn = sqlite3.connect("plants.db")

c = conn.cursor()

c.execute("SELECT user_id, name, watering_interval FROM plants WHERE next_watering

plants = c.fetchall()

conn.close()


for user_id, name, watering_interval in plants:

await context.bot.send_message(

chat_id=user_id,

text=f"🌱 Напоминание: пора полить растение '{name}'!"

)

# Обновляем время следующего полива

conn = sqlite3.connect("plants.db")

c = conn.cursor()

c.execute(

"""

UPDATE plants

SET last_watered = ?, next_watering = ?

WHERE name = ? AND user_id = ?

""",

(

datetime.now(),

datetime.now() + timedelta(days=watering_interval),

name,

user_id,

),

)

conn.commit()

conn.close()


def main():

init_db()

# Замените YOUR_BOT_TOKEN на токен вашего бота

application = Application.builder().token("YOUR_BOT_TOKEN").build()


# Обработчики

application.add_handler(CommandHandler("start", start))

application.add_handler(CallbackQueryHandler(add_plant, pattern="add_plant"))

application.add_handler(CallbackQueryHandler(glossary, pattern="glossary"))

application.add_handler(CallbackQueryHandler(watering, pattern="watering"))

application.add_handler(CallbackQueryHandler(check_watering_dates, pattern="check_watering_dates"))

application.add_handler(CallbackQueryHandler(delete_plant, pattern="delete_plant"))

application.add_handler(CallbackQueryHandler(confirm_delete, pattern="confirm_delete_"))

application.add_handler(CallbackQueryHandler(execute_delete, pattern="delete_"))

application.add_handler(CallbackQueryHandler(show_plant, pattern="plant_"))

application.add_handler(CallbackQueryHandler(back, pattern="back"))

application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))

application.add_handler(MessageHandler(filters.PHOTO, handle_photo))


# Ежедневные уведомления в 15:00

application.job_queue.run_daily(

check_watering,

time=datetime.strptime("15:00:00", "%H:%M:%S").time(),

days=(0, 1, 2, 3, 4, 5, 6) # Все дни недели

)


# Запуск бота

application.run_polling()


if __name__ == "__main__":

main()


Бот для Юных натуралистов — это практический проект, объединяющий программирование и биологию. Он помогает учащимся освоить Python, базы данных и разработку ботов, а также развивает ответственность за уход за растениями. Проект легко адаптировать под разные образовательные задачи, добавляя новые функции или упрощая интерфейс.