Написать пост

Как написать Telegram-бота на Python: делаем ремайндер

Логотип компании Elbrus Bootcamp

Создаём простого бота-ремайндера в Telegram на языке Python, единственная задача которого — напоминать пользователю о важных делах.

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

Инструкция подойдет для новичков, которые знают Python на базовом уровне, пробовали писать код и установили на компьютер редактор кода.

Первый этап: подготовка проекта и развертывание окружения

Найдем в поиске Telegram BotFather — официального бота мессенджера, который создает другие боты и управляет ими. В интерфейсе выбираем /start, затем — /newbot, и следом задаем имя и адрес. В этой иструкции это будут Elbrus Reminder и elbrus_reminder_bot соответственно.

После этого BotFather пришлет сообщение с токеном и ссылкой на бот:

Токен нужно хранить в безопасном месте — он дает контроль над ботом. и, как следствие, позволяет получить доступ к данным пользователей.

На время закроем Telegram и создадим на компьютере папку с именем проекта: например, reminder_bot. Откроем папку в среде разработки и создадим рабочий файл с понятным названием — bot.py.

Откроем терминал редактора кода и создадим для проекта новое окружение. В среде разработки с помощью команды python -m venv .venv создадим папку с окружением .venv.

Если окружение не активировалось автоматически, можно сделать это вручную, прописав путь к файлу активации в формате source .venv/bin/activate, где source — команда языка программирования Bash. Другой вариант — перезапустить среду разработки. Он работает для Visual Studio Code, но нужно предварительно принять предложение редактора привязать среду к папке проекта сразу после создания окружения.

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

Второй этап: подключаем библиотеки

Проект создан и окружение готово: пора переходить к написанию кода. По правилам хорошего тона в первую очередь через import добавляем несколько предустановленных библиотек Python. При создании бота нам пригодятся logging и time, которые отвечают за определение времени и логирование сообщений.

			import time
import logging
		

Затем добавим асинхронную библиотеку aiogram, на основе которой будет работать бот. Она, например, определяет, какое сообщение пришло, как его нужно обработать и какие порты нужны. Сначала устанавливаем ее через терминал командой pip install aiogram, а в редакторе кода пишем следующее:

			from aiogram import Bot, Dispatcher, executor, types
		

Из этой библиотеки нам нужны только отдельные модули и классы — все ее возможности для создания базовой версии бота не пригодятся. Поэтому вместо одиночного import использована команда from {} import {}.

Когда библиотеки импортированы, создадим переменные с токеном бота и сообщением, которое он будет отправлять пользователю. Вы можете заменить это сообщение на любое другое, которое вам необходимо. Это статические переменные, поэтому их имена написаны капслоком:

			TOKEN = "здесьбудетваштокенот от BotFather"
MSG = "Программировал ли ты сегодня, {}?"
		

Токены, ключи и прочие данные для настройки проекта лучше загружать более безопасным способом (например, создавать переменные окружения или файлы конфигурации). Но в данном случае сделаем все в одном файле для наглядности, а примеры более безопасной работы с такими переменными разберем в следующих постах.

Теперь создадим экземпляр класса Bot, передав ему в качестве аргумента наш токен, и экземпляр класса Dispatcher (dp), который в качестве аргумента получит bot. В результате получаем связку объекта класса bot с ключем, который привязан к боту, и диспетчера, который привязан к этому боту:

			bot = Bot(token=TOKEN)
dp = Dispatcher(bot=bot)
		

Следующим шагом добавим конструкцию под названием декоратор (massage_handler) — она помогает получить из диспетчера нужный функционал. В качестве аргумента прописываем команды, которые обрабатывает декоратор — в данном случае это команда /start, которая запускает бот.

			@dp.message_handler(commands=['start'])
		

Под декоратором прописываем функцию, которая будет обрабатывать команду /start и определяет логику, в соответствии с которой будет работать бот. Поскольку мы работаем с асинхронной библиотекой, функция тоже должна быть асинхронной. Для этого перед указанием def добавим ключевое слово async:

			async def start_handler(message: types.Message):
		

Функция приветствует пользователя и обрабатывает сообщение, которое он отправляет в ответ. Из сообщения можно получить информацию о пользователе, который его прислал, время отправки и его ID.

Создаем переменную и сохраняем в ней user id:

			user_id = message.from_user.id
		

Затем получаем из сообщения короткое и полное имя пользователя:

			user_name = message.from_user.first_name
user_full_name = message.from_user.full_name
		

Для того, чтобы в логах отображалась информация о пользователе, передаем в виде текста ID и полное имя, а также используем возможности библиотеки time, чтобы определить время, когда писал пользователь:

			logging.info(f'{user_id} {user_full_name} {time.asctime()}')
		

Здесь отойдем в сторону и проверим корректность работы модуля time. Сделать это можно в терминале: для этого напишем import time, а затем — time.asctime

Вернемся к коду. Поскольку функция, которую мы используем, асинхронна, вместо обычного для функций return используем await:

			await message.reply(f"Привет, {user_full_name}!")
		

Ответить пользователю в боте можно несколькими способами — в данном случае используем reply.Выше в переменной MSG мы задали стандартное сообщение: «Программировал ли ты сегодня, {}?». Зададим частоту напоминаний: семь раз каждые семь суток (60х60х24 — количество секунд в одних сутках) с момента отправки команды /start боту от пользователя:

			for i in range(7):
    await asyncio.sleep(60*60*24)
		

Затем настроим отправку сообщения с указанием имени пользователя в этом же цикле:

			await bot.send_message(user_id, MSG.format(user_name))
		

Третий этап: финал

Переходим к финальной части: в конце скрипта напишем несколько строк. Они могут показаться странными для новичка, но это общепринятая практика, к которой многие программисты прибегают при разработке. В этой строке мы проверяем, равна ли переменная __name__ строке "__main__". Это условие всегда будет True, если мы запускаем этот файл как python-скрипт через терминал:

			if __name__ == '__main__':
		

Теперь делаем нашего бота доступным в сети:

			executor.start_polling(dp)
		

Сохраняем файл. Запускаем бота в терминале, открытом в папке проекта, с помощью команды python bot.py.

Вернемся в BotFather и перейдем по ссылке, которую получили вместе с токеном. Нажимаем «Начать» — готово, бот, написанный меньше, чем в 30 строк, работает.

Так выглядит его код целиком:

			import time
import logging
import asyncio

from aiogram import Bot, Dispatcher, executor, types

TOKEN = "здесьбудетваштокенот@BotFather"
MSG = "Программировал ли ты сегодня, {}?"

logging.basicConfig(level=logging.INFO)

bot = Bot(token=TOKEN)
dp = Dispatcher(bot=bot)

@dp.message_handler(commands=["start"])
async def start_handler(message: types.Message):
    user_id = message.from_user.id
    user_name = message.from_user.first_name
    user_full_name = message.from_user.full_name
    logging.info(f'{user_id} {user_full_name} {time.asctime()}')
    await message.reply(f"Привет, {user_full_name}!")
    
    for i in range(7):
        await asyncio.sleep(60*60*24)
        await bot.send_message(user_id, MSG.format(user_name))

if __name__ == "__main__": 
    executor.start_polling(dp)
		

В следующий раз подробно расскажем, как написать подобный бот на языке программирования JavaScript. Подписывайтесь, чтобы не пропустить инструкцию.

Следите за новыми постами
Следите за новыми постами по любимым темам
47К открытий47К показов