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

Как написать 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. Подписывайтесь, чтобы не пропустить инструкцию.