Как создать Telegram-бота для мониторинга погоды на Python
В статье делимся пошаговой инструкцией, как написать Telegram-бота для получения данных о погоде и задеплоить его на виртуальный сервер.
Эта инструкция — часть курса «Как создавать Telegram-ботов».
Смотреть весь курс
Введение
Всем привет! Меня зовут Максим Плачковский, я автор канала PythonToday. Из этой статьи вы узнаете, как написать своего Telegram-бота для получения данных о погоде в любом городе нашей планеты. Мы детально рассмотрим работу с API, парсинг JSON и напишем бота на асинхронной библиотеке aiogram. А после — загрузим его на виртуальный сервер и запустим.
Подготовка API и рабочего окружения
Перед написанием кода нужно получить API-токены для работы с сервисом OpenWeather и Telegram-ботом, а также подготовить рабочее окружение.
Получаем токен OpenWeather
Начнем с самого простого: зарегистрируемся на официальном сайте и в разделе My API keys создадим токен.
После того, как вы создали API-ключ, дайте ему немного «отлежаться» — обычно это занимает 10-15 минут. Спустя это время можно общаться с OpenWeather с помощью сгенерированного токена.
Генерируем токен для Telegram-бота
Ключ для Telegram-бота можно получить у @BotFather, введя /newbot — команду для создания и регистрации нового бота. Во время настройки придумайте боту логин и название — например, Weather Bot. Есть также опциональные настройки: текст приветствия, изображение. Используйте, если хотите получить более уникального бота!

Импортируем необходимые библиотеки
Для работы нам понадобятся модули requests и aiogram — установим их.
pip install requests aiogram
После импортируем модули и классы в файл нашего пет-проекта.
import os import datetime import requests from aiogram import Bot, types from aiogram.dispatcher import Dispatcher from aiogram.utils import executor bot = Bot(token='your_bot_token') dp = Dispatcher(bot)
Создание бота
Первое сообщение
Для начала проверим, что aiogram увидел бота и мы можем с ним взаимодействовать. Создадим простую асинхронную функцию start_command для ответа на команду /start . И добавляем метод start_polling для запуска бота.
import os import datetime import requests from aiogram import Bot, types from aiogram.dispatcher import Dispatcher from aiogram.utils import executor bot = Bot(token='your_bot_token') dp = Dispatcher(bot) @dp.message.handler(commands=["start"]) async def start_command(message: types.Message): await message.reply("Привет! Напиши мне название города и я пришлю сводку погоды") if __name__ == "__main__": # С помощью метода executor.start_polling опрашиваем # Dispatcher: ожидаем команду /start executor.start_polling(dp)
Запускаем скрипт, заходим в Telegram и пишем команду /start — все работает: бот возвращает нужное сообщение.

Обрабатываем входные данные
Теперь добавим функцию, которая будет реагировать на текстовые сообщения с названиями городов. Она должна проверять их на корректность и выводить ошибку, если валидация не была пройдена.
@dp.message_handler() async def get_weather(message: types.Message): pass
OpenWeather должен принимать название города через API и возвращать данные о погоде. Исходя из документации, кроме названия города запрос должен содержать API-токен.

Усовершенствуем запрос: в параметр q будем передавать город, добавим параметр lang=ru , чтобы API работал с кириллицей. А также используем units=metric для установки метрической системы:
http://api.openweathermap.org/data/2.5/weather?q=москва&lang=ru&units=metric&appid=наш_токен
Теперь добавим блок try-except для обработки пользовательских запросов и создадим переменную для записи результатов OpenWeather, которые возвращает сервис в JSON-формате.
@dp.message_handler() async def get_weather(message: types.Message): try: response = requests.get( f"http://api.openweathermap.org/data/2.5/weather?q=москва&lang=ru&units=metric&appid=your_token" ) data = response.json() except: await message.reply("Проверьте название города!")
Отправим запрос через браузер и посмотрим, какая температура, например, в Бангкоке.

В ответе получаем не только данные о температуре, но и облачность, давление, влажность, время заката, рассвета и другие величины.
Парсим JSON
Приступим к парсингу ответа OpenWeather в формате JSON: заберем данные о городе и температуре, влажности, давлении и скорости ветра.
city = data["name"] cur_temp = data["main"]["temp"] humidity = data["main"]["humidity"] pressure = data["main"]["pressure"] wind = data["wind"]["speed"]
OpenWeather возвращает время рассвета и заката в формате unix timestamp. Извлечем эти данные и преобразуем в секунды.
# получаем время рассвета и преобразуем его в читабельный формат sunrise_timestamp = datetime.datetime.fromtimestamp(data["sys"]["sunrise"]) # то же самое проделаем со временем заката sunset_timestamp = datetime.datetime.fromtimestamp(data["sys"]["sunset"])
Зная время рассвета и заката, мы можем вернуть пользователю продолжительность дня. В результате получается следующее:
@dp.message_handler() async def get_weather(message: types.Message): try: response = requests.get( f"http://api.openweathermap.org/data/2.5/weather?q=москва&lang=ru&units=metric&appid=your_token" ) data = response.json() city = data["name"] cur_temp = data["main"]["temp"] humidity = data["main"]["humidity"] pressure = data["main"]["pressure"] wind = data["wind"]["speed"] sunrise_timestamp = datetime.datetime.fromtimestamp(data["sys"]["sunrise"]) sunset_timestamp = datetime.datetime.fromtimestamp(data["sys"]["sunset"]) # продолжительность дня length_of_the_day = datetime.datetime.fromtimestamp(data["sys"]["sunset"]) - datetime.datetime.fromtimestamp(data["sys"]["sunrise"]) except: await message.reply("Проверьте название города!")
Декорируем сообщения
Разнообразим текст сообщений — будем выводить разные эмодзи в зависимости от погоды.
У словаря, который мы ранее спарсили, есть ключ main — он хранит описание погоды. На его основе мы можем создать свою коллекцию с эмодзи под разные ситуации.
code_to_smile =
Также напишем условие, чтобы в случае специфической погоды, для которой эмодзи не предусмотрен, программа не ломалась, а выводила специальное сообщение.
# получаем значение погоды weather_description = data["weather"][0]["main"] if weather_description in code_to_smile: wd = code_to_smile[weather_description] else: # если эмодзи для погоды нет, выводим другое сообщение wd = "Посмотри в окно, я не понимаю, что там за погода. "
Возвращаем данные пользователю
Данные собрали — время отдать их пользователю. Сформируем строки с датой и временем, погодой, влажностью и другими данными. Главное — учесть формат данных. Например, OpenWeather возвращает значение давления в гектопаскалях. Так, если вы хотите отдавать пользователю данные в миллиметрах ртутного столба, значение нужно разделить на 1.33 и округлить в большую сторону.
await message.reply(f""\n f"Погода в городе: \nТемпература: °C \n" f"Влажность: %\nДавление: мм.рт.ст\nВетер: м/с \n" f"Восход солнца: \nЗакат солнца: \nПродолжительность дня: \n" f"Хорошего дня!" )
Супер — бот работает и возвращает данные в удобочитаемом формате!

Деплой бота
Сейчас синоптик запущен на компьютере. Это неудобно, если вы хотите общаться с ним круглосуточно. Ведь тогда нужно поддерживать бесперебойную работу компьютера и постоянное соединение с интернетом.
Облачные серверы с гибкой производительностью ядра vCPU
Бота лучше перенести в облако. Поскольку затраты процессора на работу с простым Open Weather API минимальны, будет достаточно виртуального сервера с 1 vCPU и 1 ГБ оперативной памяти. С учетом выделенного IP-адреса такая конфигурация выйдет примерно в 30 ₽/день.
Для начала зарегистрируемся в панели управления и создадим новый сервер в разделе Облачная платформа. Затем настроим его.

Далее подключимся к серверу по SSH, создадим виртуальное окружение для бота и загрузим его удобным способом.

Последним шагом нужно создать .service-файл, который будет автоматически поднимать бота в случае перезагрузки сервера.


Запускаем командой: systemctl enable tg_bot.service
Проверяем статус: systemctl status tg_bot.service
И перезапускаем .service-файл: systemctl restart tg_bot.service

Все готово: бот стабильно работает на сервере и автоматически поднимается в случае перезагрузки.

Заключение
Следуя инструкции, каждый может создать бота для мониторинга погоды. А использование виртуальной машины из линейки Shared Line позволит избежать капитальных затрат. Платите только за потребленные ботом мощности по модели pay-as-you-go, а также экономьте на неполной загрузке ядра.
Автор: Максим Плачковский, создатель канала PythonToday
Как подключить платежную систему с Payments к Telegram
Как разработать веб-приложение (интернет-магазин) в Telegram
Зарегистрируйтесь в панели управления
И уже через пару минут сможете арендовать сервер, развернуть базы данных или обеспечить быструю доставку контента.
Как написать бота в Телеграме на Python с нуля — пошаговая инструкция
Боты в Телеграме — это уже давно не новинка: каждый уважающий себя интернет-магазин внутри социальной сети обладает возможностью удобной навигации по меню, и даже некоторые новостные каналы настраивают таргет по пожеланиям пользователя с помощью автоматизированных систем.

Вопрос остается в другом: как сделать бота так, чтобы не растратить миллионы на отладку и работу? Несмотря на то, что существует огромное количество автоматических конструктов, все равно нужно потратить немало времени на разбор алгоритма и итогового кода. К тому же они не могут гарантировать корректную работу абсолютно всех функций. Например, когда кто-то захочет связать курс доллара с днями проигрышей «Спартака», ему придется самостоятельно настраивать логику в собственной программе.
Программирование — вот, за чем стоит создание качественного бота. И мы попросту не могли обойти эту тему стороной, потому что с помощью Python создать автоматизированную систему в Телеграме проще простого. Копируем код, исследуем каждую строчку и удивляемся результатам!
Действие 1. Немного теории-базы для понимания принципа работы API Telegram
В первую очередь необходимо понять, каким образом бот будет производить отправку сообщений с прочтением ответов от пользователей.
Разработчики Telegram создали функционал для удобного использования API HTML. Он, в свою очередь, зависит от URL, общий вид функции:
При этом #названиеметода# — может быть как getChat (открыть чат), sendMessage (отправить сообщение), так и getUpdates (обновление). Для подтверждения подлинности бота в системе внутри каждой программной строки указывается токен — специальные символы, создающиеся при формировании бота.

Общий вид токена представлен далее:
При этом стоит понимать, что большая часть методов требует предоставления дополнительных параметров. В случае с sendMessage — непосредственный текст и chat_id. Именно поэтому внутри ботов реализуются как POST, так и GET запросы, которые передаются как URL-строки (например, application/#x-www-form-urlencoded#) и могут быть представлены только в кодировке UTF-8.
После того, как API-функция получает запрос, бот отправляет ответ формата JSOM. Предположим, что ранее был выполнен запрос данных при помощи метода getME, в таком случае пользователь получит ответ следующего вида:
В том случае, если значение поля «ok» равно true, результат обработки появится в поле, именованном «field». В ином случае текст ошибки можно будет найти в «description».

Второй вопрос, который необходимо изучить — каким образом бот будет получать сообщения от пользователей, и как реализуется дальнейшая обработка?
Несмотря на то, что во многих языках программирования поддерживаются многомиллионные библиотеки со схожими алгоритмами, разработчики остановились на двух методах:
Первый — ручная обработка методов при помощи getUpdates. То есть, бот будет получать объекты-массив формата Update. При этом сама функция работает как длинный опрос, то есть, изначально пользователи отправляют запросы, система обрабатывает их, выдает результат, и идет откат к первому действию, пока не закончится работа бота. Если не хочется производить повторную работу и тратить вычислительные мощности, лучше задуматься над параметром offset.

Метод getUpdates настоятельно рекомендуется использовать, если:
- Нет желания формировать произвольную настройку HTTPS.
- Производится работа со скриптовыми языками, трудно встраиваемыми в деятельность веб-серверов.
- Бот находится время от времени в высокой нагрузке.
- Периодически производится смена сервера для бота.
Второй — использование метода setWebhooks. Таким образом Телеграм будет автоматически перенаправлять все запросы на конкретно указанный URL сразу же, как они будут появляться. При этом необходимо заранее позаботиться о подготовке HTTPS-сертификата, либо создать новые, но обязательно их заверить.
Метод Webhooks является одним из оптимальных в том случае, если:
- Используется веб-язык для дополнения программирования (PHP, HTML).
- Бот не страдает от высокой нагрузки и нет необходимости самостоятельной обработки запросов.
- У бота есть стабильное место внутри веб-сервера.
Третий, не менее важный вопрос: каким образом производится регистрация бота внутри социальной сети Телеграм?
Для работы со всеми ботами необходимо ознакомиться с официальным каналом @BotFather. Он не только формирует базовую настройку для всех ботов, но и помогает с персонализацией, например, встроенной поддержкой, фотографией, описанием и тому подобным.
Касательно Python, одна из наиболее часто используемых библиотек — PyTelegramBotAPI. Она написана в соответствии с базовыми принципами ООП и является лишь оболочкой для HTML-запросов. При этом все типы данных представляют собой отдельные классы.
Действие 2. Подготовка плацдарма для взлета бота
Основа бота на Python — новейшая библиотека PyTelegramBotAPI (в простонародье «Телебот»). Прежде чем начинать работу непосредственно с программированием, необходимо убедиться, что на компьютере установлена актуальная версия программного обеспечения. Функция для Linux:

После этого при помощи командной строки (Windows) или терминала (Linux) необходимо произвести установку библиотеки:
На этом все приготовления закончены — настоятельно рекомендуется использовать версию Python 3-го поколения (3.7+). Это избавит от проблем с совместимостью, наиболее актуальные версии расположены на официальном сайте.
Действие 3. Настройка речевого аппарата бота
Вне зависимости от того, в какой именно среде будет писаться код (блокнот, Jupyter Notebook), программирование будет одинаковым — использование long pool, много упорства и немало фантазии под соусом из функционала библиотеки Телебота.

В первую очередь производится импорт библиотеки с подключением токена (получить его можно у @BotFather):
import telebot;
bot = telebot.TeleBot(‘#токен#’);
Для получения сообщений от бота необходимо дополнительно подключить метод:
@bot.message_handler(content_types=[‘text’]) //Получатель сообщения
def get_text_messages(message): //Метод обработки сообщения
При этом стоит понимать, что поле «content_types» может принимать различные значения в зависимости от ситуаций — это может быть набор-массив, числа, знаки и тому подобное. Например:
Благодаря указанной функции бот будет реагировать на все присланные текстовые, видео- и аудиосообщения. Более подробно о рассматриваемых типах мессенджей можно прочитать в официальной документации Телебота.
Дальнейший код является сугубо демонстрационным и лишь описывает основной синтаксис взаимодействия библиотеки Телебот и Python. Постараемся создать такую программу, чтобы пользователь писал «Привет», а в ответ получал эксклюзивный «Привет от Партнеркина!».
if message.text == «Привет»:
bot.send_message(message.from_user.id, «Привет от Партнеркина!»)
elif message.text == «/help»:
bot.send_message(message.from_user.id, «Напиши «Привет»)»)
else:
bot.send_message(message.from_user.id, «Партнеркин помогает с /help»)
Каждая из указанных функций характеризует поведение пользователя — что именно он может ввести, и как на это должен реагировать бот. Остается добавить только одну строку, которая работает только с методом long pool:
Вне зависимости от того будет ли находиться пользователь в диалоге, каждую секунду бот будет спрашивать непосредственно у социальной сети «Пишет ли мне этот пользователь в этом диалоге?». Постепенно расширяясь на множество диалогов, возможно увеличение времени задержки.
Действие 4. Добавление логики и мозгов
Взаимодействие с пользователем посредством угадывания каждой его фразы — это, конечно, весело. Однако мы постараемся расширить функционал, введя количество покупок, наименования и стоимость. В этом нам потребуется следующий метод register_next_step_handler:
name = »;
count = 0;
price = 0;
@bot.message_handler(content_types=[‘text’])
def start(message):
if message.text == ‘/reg’:
bot.send_message(message.from_user.id, «Что хочешь купить?»);
bot.register_next_step_handler(message, get_name); //следующий шаг — функция get_name
else:
bot.send_message(message.from_user.id, ‘Напиши /reg’);
def get_name(message):
global name;
name = message.text;
bot.send_message(message.from_user.id, ‘Сколько товара ты хочешь купить?’);
bot.register_next_step_handler(message, get_surnme);
def get_surname(message):
global surname;
surname = message.text;
bot.send_message(‘Итоговая стоимость составит’);
bot.register_next_step_handler(message, get_price;
def get_price(message):
global price;
while price == 0: #проверка, что цена не равна 0
try:
price = count*100 #проверка, что
except Exception:
bot.send_message(message.from_user.id, Некорректное количество товара);
bot.send_message(message.from_user.id, ‘Итоговая стоимость ‘+str(age)+’ за ‘+name+’ в количестве ‘+price+)

Это один из наиболее простых способов взаимодействия с ботом, не используя дополнительно базы данных, то есть, не храня значения и не передавая их. Для того, чтобы взаимодействие с пользователем стало более продуктивным и красочным, попробуем попросить его подтвердить заказ при помощи кнопок. Для этого потребуется внести небольшие коррективы в функцию get_price:
def get_price(message):
global price;
while price == 0: #проверка, что цена не равна 0
try:
price = count*100 #проверка, что
except Exception:
bot.send_message(message.from_user.id, Некорректное количество товара);
keyboard = types.InlineKeyboardMarkup(); //кнопочки
key_yes = types.InlineKeyboardButton(text=’Да’, callback_data=’yes’); #кнопка «Да»
keyboard.add(key_yes); #добавление кнопки в клавиатуру
key_no= types.InlineKeyboardButton(text=’Нет’, callback_data=’no’);
keyboard.add(key_no);
question= ‘Итоговая стоимость ‘+str(age)+’ за ‘+name+’ в количестве ‘+price+’?’)
bot.send_message(message.from_user.id, text=question, reply_markup=keyboard)
Вне зависимости от того, что именно нажмет пользователь, настоящий код не будет переводить его куда-либо дальше. А все потому, что до сих пор не был прописан код-обработчик:
@bot.callback_query_handler(func=lambda call: True)
def callback_worker(call):
if call.data == «yes»:
. //обработка или сохранение результата
bot.send_message(call.message.chat.id, ‘Принято’);
elif call.data == «no»:
. #код начинает работу сначала
Самый последний шаг — добавить типы из начальной библиотеки:
Действие 5. Расширение функционала
Функционал Телебота поистине безграничен — постоянно добавляются новые фишки, пользователи демонстрируют исключительные функции, которые в режиме реального времени позволяют не столько взаимодействовать с пользователями, сколько снимают данные и фиксируют сигналы для работы (с валютами, например).
Но касательно работы с пользователями, разработчики постарались по максимуму, добавив, например, целых два типа кнопок:
Классическая RelpyKeyboardMarkup — кнопки расположены непосредственно под полем ввода сообщения:

Современная InlineKeyboardMarkup — кнопки привязаны к конкретному сообщению:

Также стоит понимать, что полноценные проекты все равно рано или поздно потребуют дополнений в виде активных баз данных. Для организации подобного функционала рекомендуется использовать docker-compose — он объединяет множество массивов в один контейнер. Данная библиотека позволяет не только создавать новые базы данных, но и налаживает связь между существующими.
Эксперты отвечают
М Михаил
Телеграм-боты могут всё, что угодно?
Вынужден разочаровать тех, кто представляет ботов, как инструмент исключительно для автоответов и искусственной активности. Думайте о Telegram боте как об автоматизированной учетной записи, которая может выполнять для вас некоторые действия. Например, вы хотите поделиться ссылкой YouTube в групповом чате, но у вас пока нет ссылки.
Если планируется работа с достаточно крупными проектами, рекомендуется также начать работу с виртуальным выделенным сервером (VPS). В отличие от стандартного AWS он обладает большими возможностями для гибкого программирования, а также устанавливается в процессе использования библиотеки Docker.
Вывод
Написать свой собственный код для бота — значит, быть уверенным в его полной работоспособности, предполагать всевозможные тесты и уметь отлаживать нерабочие куски в режиме реального времени. Современные технологии позволяют овладеть языком программирования за считанные часы — так почему бы не воспользоваться такой возможностью для автоматизации работы в Телеграме?
А если у вас за плечами часы практики или есть готовое портфолио по созданию подобных ботов, то смело можно размещать свое резюме в нашем специальном разделе, где заказчики ищут исполнителей для своих проектов.
Как создать чат-бота для Telegram на языке Python: детальная инструкция

Уроки
На чтение 12 мин Просмотров 49 Опубликовано 12.06.2023 Обновлено 12.06.2023
С появлением мессенджера Telegram и его популярности, создание чат-ботов для данной платформы стало актуальным и интересным направлением разработки. Одним из языков программирования, который широко используется при создании чат-ботов, является Python. В этой статье мы рассмотрим пошаговую инструкцию по созданию чат-бота для Telegram на языке Python.
Для создания чат-бота нам понадобится библиотека python-telegram-bot, которая предоставляет удобный интерфейс для работы с Telegram API. Также нам потребуется токен, который мы получим у @BotFather, официального бота Telegram для создания других ботов.
В статье мы рассмотрим шаги по созданию базового чат-бота, который будет реагировать на команды пользователя и отправлять ему сообщения в ответ. Также мы рассмотрим возможность расширения нашего бота путем подключения к базе данных и создания дополнительных функций.
Подготовка
Перед тем, как начать создание чат-бота для Telegram на языке Python, необходимо подготовить рабочую среду. В первую очередь, необходимо установить последнюю версию Python с официального сайта python.org. Не рекомендуется использование Python версии ниже 3.6, так как некоторые библиотеки могут быть несовместимы.
Также потребуется установить пакетный менеджер pip, который позволит управлять установкой и обновлением библиотек Python. Пакетный менеджер может быть установлен как часть Python, либо отдельно.
Один из технологических стеков для создания чат-ботов на Python – это библиотека python-telegram-bot. Она решает большинство задач, связанных с созданием ботов для Telegram. Для ее установки нужно выполнить команду в терминале: pip install python-telegram-bot.
- Установка Python версии 3.6 и выше.
- Установка пакетного менеджера pip.
- Установка библиотеки python-telegram-bot.
Не забудьте создать бота в Telegram, получить токен и добавить его в код вашего бота. В качестве редактора кода можно использовать любую среду разработки на ваше усмотрение: PyCharm, Visual Studio Code или другую. Главное, чтобы в вашей среде была установлена соответствующая версия Python и был подключен пакетный менеджер pip.
Шаг 1. Регистрация и настройка аккаунта на Telegram
Telegram – один из самых популярных мессенджеров в мире, созданный Павлом Дуровым. Для того чтобы начать работать с Telegram и создавать чат-боты на языке Python, необходимо зарегистрироваться и настроить аккаунт. Процесс регистрации и настройки достаточно простой и быстрый.
Шаг 1.1. Установка Telegram на устройство
- Для использования Telegram необходимо скачать и установить приложение на свое устройство – смартфон, планшет или компьютер.
- Скачать приложение можно с официального сайта или из App Store / Google Play.
Шаг 1.2. Регистрация аккаунта
- После установки приложения Telegram на устройстве необходимо зарегистрироваться. Для этого нужно ввести свой номер телефона, на который придет код подтверждения.
- После ввода кода подтверждения в Telegram вы сможете настроить свой профиль – указать имя и фотографию.
Шаг 1.3. Настройка аккаунта
- Чтобы обеспечить безопасность своего аккаунта на Telegram, рекомендуется включить двухфакторную авторизацию и настроить пароль-приложение.
- Также можно изменить настройки конфиденциальности – определить, кто может писать вам личные сообщения, кто сможет видеть ваше онлайн-состояние и другие параметры.
Читать: Сессии в Flask: как создать и использовать их в приложении
После выполнения шага 1 вы будете готовы к созданию чат-бота в Telegram на языке Python. Для этого необходимо получить API ключ от BotFather, который будет использоваться для взаимодействия между ботом и пользователем.
Шаг 2. Создание нового бота и получение токена
Для создания нового бота в Telegram вам необходимо обратиться к специальному боту-контроллеру, который называется “BotFather”.
Для этого откройте Telegram и найдите бота BotFather. Напишите ему любое сообщение и следуйте его инструкциям. Далее вам потребуется выбрать имя для вашего бота и получить уникальный токен доступа.
Токен является уникальным ключом доступа к вашему боту. Он необходим для обмена сообщениями между вашим ботом и серверами Telegram. Сохраните токен в безопасном месте, чтобы иметь к нему доступ в будущем.
Чтобы обработать входящие сообщения от пользователей, ваш бот должен отправлять запросы к серверам Telegram, используя HTTP API. Токен должен быть включен в каждый запрос к серверам Telegram для аутентификации вашего бота.
Когда вы получили токен доступа, вы можете приступать к созданию вашего чат-бота на языке Python. Следующим шагом будет настройка и запуск вашего сервера, который будет обрабатывать входящие запросы от пользователей и отправлять ответы через Telegram.
Шаг 3. Установка библиотек и настройка окружения разработки
Для создания чат-бота для Telegram на языке Python необходимо настроить среду разработки и установить необходимые библиотеки.
Среда разработки – это программа, которая позволяет писать и запускать код. Рекомендуется использовать PyCharm – одну из самых популярных сред разработки на языке Python.
Далее необходимо установить библиотеку python-telegram-bot – она предоставляет доступ к API Telegram и необходима для создания чат-бота. Для установки можно воспользоваться менеджером пакетов pip и выполнить следующую команду:
- pip install python-telegram-bot
Также может потребоваться установить дополнительные библиотеки, например, requests или BeautifulSoup. Для этого необходимо выполнить аналогичную команду:
- pip install requests
- pip install beautifulsoup4
После установки библиотек необходимо создать новый проект в среде разработки и настроить интерпретатор Python для проекта. Для этого можно воспользоваться меню Preferences (Settings) -> Project Interpreter и выбрать нужный интерпретатор.
Также возможно использовать виртуальное окружение – это отдельная изолированная среда для каждого проекта, в которой можно устанавливать свои библиотеки без влияния на другие проекты. Для создания виртуального окружения можно воспользоваться встроенным модулем venv в Python:
| Команда | Описание |
| python3 -m venv myenv | Создание виртуального окружения в папке myenv |
| source myenv/bin/activate | Активация виртуального окружения |
В результате выполнения этих действий мы получим изолированную среду, в которой можно устанавливать и использовать нужные библиотеки.
Написание кода
Для написания чат-бота на языке Python для Telegram необходимо использовать библиотеку pyTelegramBotAPI. Для ее установки достаточно воспользоваться командой “pip install pyTelegramBotAPI”.
Дальше необходимо создать файл с расширением .py и импортировать необходимые модули: telebot, config и др. Далее необходимо создать экземпляр класса TeleBot и передать ему токен бота. Токен можно получить у BotFather.
Далее необходимо описание функций обработки команд и сообщений, которые будет обрабатывать чат-бот. Для этого можно использовать декораторы, например, @bot.message_handler(commands=[‘start’]) – для обработки команды /start, или @bot.message_handler(content_types=[‘text’]) – для обработки текстовых сообщений.
Для отправки сообщений можно использовать методы класса TeleBot, например, bot.send_message(chat_id, text). Для отправки изображений – bot.send_photo(chat_id, photo). Также можно использовать inline-клавиатуру, используя метод bot.send_message с параметром reply_markup.
В процессе написания кода можно использовать функции создания базы данных, логирования, обработки ошибок и др. Также, полезно использовать цикл, который будет работать бесконечно, чтобы чат-бот мог обрабатывать сообщения 24/7.
- Итак, основными шагами написания кода являются:
- Импортирование необходимых модулей и создание экземпляра класса TeleBot с передачей токена бота;
- Описание функций-обработчиков команд и сообщений;
- Использование методов класса TeleBot для отправки сообщений и inline-клавиатуры;
- Использование функций создания базы данных, логирования и обработки ошибок;
- Создание бесконечного цикла для бесперебойной работы чат-бота.
Читать: Изучаем основы библиотеки Pandas в Python: работа с таблицами данных
Шаг 4. Импорт и инициализация библиотек
Для создания чат-бота в Telegram на языке Python нам потребуются специальные библиотеки. В данном шаге мы рассмотрим импорт и инициализацию необходимых библиотек.
1. python-telegram-bot
Это главная библиотека, которая позволяет создавать чат-ботов для Telegram на языке Python. Для ее установки используется команда:
- pip install python-telegram-bot
После установки нам необходимо импортировать библиотеку и создать объект-интерфейс для дальнейшей работы:
| import telegram | # импортирование библиотеки |
| bot = telegram.Bot(token=»TOKEN») | # инициализация объекта-интерфейса с токеном |
2. logging
Библиотека logging используется для логирования действий бота. Чтобы ее импортировать, нужно выполнить следующую команду:
После импорта можно задать уровень логирования и выводить логи в файл или на экран:
| logging.basicConfig(format=’%(asctime)s — %(name)s — %(levelname)s — %(message)s’, level=logging.INFO) | # задание формата вывода и уровня логирования |
| logger = logging.getLogger(__name__) | # создание объекта-логгера для текущего модуля |
3. configparser
Библиотека configparser используется для работы с конфигурационными файлами, которые могут содержать настройки бота. Чтобы ее импортировать, нужно выполнить следующую команду:
- import configparser
Далее, мы можем создать объект-парсер и загрузить настройки из файла:
| config = configparser.ConfigParser() | # создание объекта-парсера |
| config.read(‘config.ini’) | # загрузка настроек из файла |
Использование этих библиотек существенно упрощает создание и поддержку чат-бота для Telegram на языке Python.
Шаг 5. Создание класса бота и функций-обработчиков сообщений
При создании бота для Telegram необходимо создать класс, который будет обрабатывать действия пользователя и отправлять ответы на эти действия. Для этого можно использовать библиотеку python-telegram-bot, которая предоставляет множество инструментов для работы с Telegram API.
В классе необходимо определить функции-обработчики сообщений, которые описывают поведение бота при отправке и приеме сообщений от пользователя. Например, функция-обработчик может отправлять определенный текстовый ответ при получении команды от пользователя.
Для обработки команд можно использовать декораторы, например, @bot.command(‘start’). Когда пользователь отправляет команду /start, бот автоматически вызывает функцию-обработчик, связанную с этой командой.
Также можно определить функции-обработчики для обработки текстовых сообщений, фото, видео и других типов сообщений. Для этого можно использовать декораторы @bot.message_handler(content_types=[‘text’]) и т.д.
В итоге, создав класс бота и определив функции-обработчики, можно запустить бота и начать его использование. Для этого необходимо вызвать метод start_polling() у объекта класса бота. После этого бот будет начинать обрабатывать сообщения и отправлять ответы на них.
Запуск и тестирование бота
Как только вы создали своего бота для Telegram на языке Python, вы должны запустить его, чтобы узнать, насколько он хорошо работает.
Чтобы запустить бота, вам необходимо запустить его скрипт на своем компьютере, используя терминал или командную строку.
Чтобы протестировать своего бота, вы можете отправить ему сообщения, используя свой аккаунт на Telegram. Бот должен ответить на ваши сообщения правильно и реагировать на команды, которые вы установили в своем коде.
Вы также можете провести более полный тест, используя unit-тестирование, чтобы убедиться, что ваш код работает правильно в любых условиях.
Кроме того, вы можете настроить логирование своего бота, чтобы увидеть все сообщения, которые он получает и отправляет. Это поможет вам выявить любые проблемы с его функционированием и исправить их.
В целом, запуск и тестирование вашего бота на Telegram позволит вам узнать, насколько хорошо его функции работают и убедиться, что он полностью соответствует вашим требованиям и ожиданиям.
Шаг 6. Запуск бота в Telegram
После того, как мы написали код на языке Python для нашего бота и добавили необходимые токены и настройки, мы можем запустить бота в Telegram и начать им пользоваться.
Для запуска бота нам потребуется запустить скрипт на языке Python с помощью команды в терминале:
python bot.py
Если все настройки прописаны правильно, то бот успешно подключится к Telegram и будет готов принимать сообщения и команды пользователей.
Читать: Часто задаваемые вопросы о едином входе Docker контейнера
Для тестирования бота вы можете просто написать ему сообщение в Telegram и посмотреть на ответ. Также вы можете использовать специальные команды, которые вы добавили в коде, чтобы получить определенные ответы от бота.
Важно помнить, что при разработке и использовании чат-бота для Telegram необходимо соблюдать правила и ограничения, установленные сервисом. Например, запрещено использовать бота для спама, распространения вирусов или проведения незаконных действий.
В целом, создание чат-бота для Telegram на языке Python не является сложной задачей, но требует определенных знаний и умений. Однако, благодаря гибкости и функциональности Telegram API, вы можете создать бота, который будет решать различные задачи и упрощать жизнь пользователям Telegram.
Шаг 7. Тестирование и доработка бота
После того, как чат-бот настроен и готов к работе, необходимо провести тестирование его функционала. Для этого можно использовать тестовый аккаунт в Telegram или пригласить друзей для помощи в тестировании.
В процессе тестирования можно обнаружить ошибки и неточности в работе бота. Если такие проблемы возникают, необходимо приступать к их доработке. Важно учесть, что бот должен быть удобен и интуитивно понятен для пользователей, поэтому корректировки должны проводиться с учетом этого фактора.
Одна из основных задач при тестировании – проверка работы бота на разных устройствах и операционных системах. Важно убедиться, что чат-бот работает стабильно на всех платформах, на которых будет использоваться.
Если все проблемы были исправлены и чат-бот прошел успешное тестирование, можно начать использовать его на практике. Нельзя забывать, что бот не является законченным продуктом и может быть доработан и усовершенствован по ходу использования.
- Проведите тестирование функционала чат-бота на разных устройствах и операционных системах;
- Исправьте обнаруженные ошибки и неточности в работе бота;
- Убедитесь, что чат-бот удобен и интуитивно понятен для пользователей;
- Начните использовать чат-бот на практике;
- Не забывайте дорабатывать и усовершенствовать бота по мере использования.
Вопрос-ответ:
Для чего создавать чат-бота для Telegram на языке Python?
Создание чат-бота для мессенджера Telegram на языке Python позволяет автоматизировать большинство рутинных задач, а также организовать такой инструмент для более быстрого и удобного общения с клиентами и подписчиками.
Какие основные преимущества использования чат-бота для Telegram?
Основные преимущества использования чат-бота для Telegram включают в себя: удобство и быстроту общения с клиентами или подписчиками, возможность автоматизации рутинных задач и повышение эффективности работы, а также улучшение качества обслуживания.
Какие навыки нужны для создания чат-бота для Telegram на языке Python?
Для создания чат-бота для Telegram на языке Python необходимо иметь базовые знания программирования на Питоне, понимание принципов работы Telegram API, а также навыки работы с библиотеками для создания чат-бота, например, python-telegram-bot.
Как начать разработку чат-бота для Telegram на языке Python?
Для начала разработки чат-бота для Telegram на языке Python нужно зарегистрироваться на платформе Telegram и получить токен бота, после чего установить необходимые библиотеки и создать центральный класс для обработки сообщений.
Можно ли добавить дополнительные функции в чат-бота для Telegram?
Да, можно. В чат-бот можно добавить дополнительные функции, например, отправку изображений, видео, аудио, голосовых сообщений и т.д. Для этого нужно использовать соответствующие методы в Telegram API и настроить обработку соответствующих сообщений в коде чат-бота.
Можно ли ограничивать доступ к чат-боту для Telegram?
Да, можно. Доступ к чат-боту для Telegram можно ограничить с помощью управления правами доступа участников. Например, можно ограничить доступ только тем пользователям, которые прошли идентификацию, или ограничить доступ к определенным функциям бота для определенных категорий пользователей.
Видео:
Введение в чат боты на языке Python
Введение в чат боты на языке Python by MoscowPython Streamed 7 months ago 1 hour, 27 minutes 3,243 views
Создаем библиотеку для бота telegram
История об изучении использовании python для написания бота библиотеки для ботов в telegram.
Код, описанием и историей которого является эта статья доступен по ссылке.
Предыстория
Как-то в разговоре со знакомым, на тему проф ориентации и общей перспективности в смысле бабла, прозвучал вопрос: «Пишешь ли ты на питоне?»
Я ответил в том смысле, что на любых животных писать неудобно. Они либо кусаются, либо убегают, а часто одновременно и от этого адски страдает каллиграфия. А на круглых, к которым относится питон, как самоходный шланг, и пытаться-то глупо.
Оказалось, что речь шла не о хладнокровной, а об электронной сущности, в смысле языка программирования: «Знаешь о таком?»
Разумеется, я знал и, за следующие 15 секунд, я вывалил на собеседника все мои познания о том, что это очередной Н-плюс-первый птичий язык, на котором пишет довольно много народу, правда не особо понятно что именно и зачем, однако в моей жизни его необходимость в нем равна приблизительно нулю. «Хот нет, вру. Пацан для колледжа делал какую-то домашку именно на питоне и просил помочь там с чем-то. Писал какого-то «бота» для телеграмма.»
В ответ я узнал, что именно питон и именно телеграмм — это крайне модно, молодёжно, современно и вообще: бабы кончают, дети смеются.
— Ты хотя бы пробовал?
— Ну, это же телеграмм ставить надо… Это же какой-то чат в телефоне, правильно?
— Ясно с тобой все.
Рюмка чая, вместе с нашим с ним разговором, подошли к концу, но идея запала. Тем более, что у меня как раз была неделя, в которую я был практически абсолютно свободен аж до следующего понедельника. «Аж кончают… да ну нафик, брехня. Но глянуть можно… Вай бы и не нот, так сказать?».
Начало
Чайник вскипел, чай налит, комп гудит: «Ну-с, и чего там оно этот питон?… Эй Влад, та на чем писал на этой фигне? Бесплатная? Ага качается. А писал чего? Бота? Ясно».
О самом питоне мне сказать особо нечего. В процессе поиска я наткнулся на фразу: «На python написано самое большое количество кода в мире», с которой теперь, спустя пару недель я бы поспорил.
Про «накрутку» для статистики
Если выкинуть из текста программ на питоне начальные пробелы и клепанный «self.», то рейтинг количества кода можно смело делить вдвое.

Ну да пусть она остается на совести создателя статьи, к делу это не относится.
В этой статье я не буду описывать ни как скачать PyCharm или десктопную версию телеграмма (зарегистрироваться в которой оказалось той еще проблемой, кстати), ни как создать своего бота и получить API Key. Этой 30-секундной информацией интернет просто завален. Будем подразумевать, что потенциальному читателю это либо вообще до фени, либо он способен эти операции произвести самостоятельно.
Переходим к сути
А суть в том, что после кое-какой настройки среда для написания кода готова, найдена и скачана библиотека для написания ботов в телеграмм, бегло просмотрены ее примеры, пастнуты в редактор кода и вот первый бот уже умеет говорить: «Я увидел: ‘ваш текст’».
В качестве библиотеки я выбрал « aiogram ». Просто потому, что первые две попавшиеся были какие-то вообще крайне печально документированные. Ну да «опенсорс–же—блин», так что удивлен я не был, просто искал что-то следующее, где хотя бы примеры были на часть функционала. О своем выборе библиотеки не жалею. Она успешно работает, все свои функции выполняет без нареканий. Другое дело, что 60-80% ее функционала мне не пригодилось, но об этом будет чуть позже.
В принципе, можно использовать вообще любую библиотеку, реализующую АПИ телеграмма. Все они идентичны до синтаксиса, отличаясь нюансами наполнения.
Кратко о функционировании ботов для телеграмм
Бот – это http клиент, который посылает серверу телеграмм команды, получает в ответ результат их выполнения и, параллельно, события, которые произошли в области интереса бота с момента последнего запроса. Сам бот может быть запущен где угодно, лишь бы с того утюга, на котором он функционирует, был доступ к сайту телеграмма. Больше требований к месту его расположения никаких нет.
Неудобство заключается в клиент-серверной идеологии самого АПИ. Реализация АПИ нас не волнует вообще, т.к. мы общаемся уже через библиотеку прокладку, но нас волнует событийность модели. Все библиотеки для работы с телеграмом которые я видел реализованы абсолютно одинаково:
- есть бесконечный цикл запросов на получение новых событий от сервера
- при получении событий дергаются куски пользовательского кода
Пользовательский код можно описывать с разной степенью «задекорированности», но так или иначе это всегда должен быть какой-то конечный кусок, реализующий действия по каждому элементарному событию. Учитывая то, что основных события всего два: текст сообщения и нажатие на кнопку в сообщении, то в пользовательской части весь код, по сути, состоит из вызова всего двух функций.
В этом заключается основная проблема т.к. реализовать сколько-нибудь сложную логику на двух асинхронно вызываемых функциях, дергаемых по каждому событию крайне сложно. Разумеется, я ознакомился с «типовыми» подходами по написанию кода в этих условиях. Я знаю о наличии декораторов с кучей условий, а так же хорошо знаком с концепцией конечного автомата.
Возможно из-за того, что я некоторые решения представляю себе довольно хорошо, я еще на берегу понял, что это тупиковый путь. Реализовать бота на событийной модели можно, но для этого нужно четкое описание логики «высеченное в граните». У меня же нет четко поставленной задачи по написанию чего-то конкретного, а титанизм усилий при попытке заменить на половине дороги логику обработки или используемый инструментарий я представлял замечательно, поэтому я даже пробовать не стал делать что-то сложнее примеров для ознакомления.
Второй «печальный» момент, с которым придется столкнуться при написании ботов – это, хм… невменяемость протокола. Прямо по нему видно, что его писали кусками в разное время и при добавлении чего-то нового ничего из старого никогда не менялось. В результате в протоколе нет практически ни одной сходной вещи, которые бы функционировали одинаково. Пара примеров. Есть два типа клавиатур, которые можно привязать к сообщению: INLINE и KEYBOARD .
Кроме слова «клавиатура» них нет ничего общего. Кнопки у них описываются по разному, имеют разный набор полей и даже сообщения с этими клавиатурами ведут себя различно и несовместимо. Сообщения с картинкой и типом «photo» – это отдельная сущность, которая с другими сообщениями имеет мало общего. У него даже текст подписи называется «caption» вместо «text». Сообщения с другими «вложениями» — это опять отдельные сущности.
В общем, попытка унифицировать этот винегрет обещает длительную и плодотворную работу методом тыка. Не очень удивительно то, что основные библиотеки для ботов даже не пытаются эту работу делать. Впрочем, к текущей статье это имеет теоретическое отношение.
Итого, задача
После того, как мой этап знакомства с АПИ телеграмма и используемой библиотеки привел меня к знаниям, описанным в паре прошлых абзацев, я отложил попытки написать что-то цельное и начал искать готовые решения по «очеловечиванию» реализации «бизнес логики» бота.
К огромному моему удивлению я не нашел вообще ничего. Я сейчас имею в виду не вебовские «конструкторы» ботов, коих миллион (ну или приблизительно столько), у меня все же стояла задача по изучению питона, а не по тренировке мелкой моторики правой руки компьютерной мышью. Я имею в виду какие-то библиотеки, которые бы позволяли реализовывать логику работы конечного продукта (бота) в привычной линейной форме, полностью убирая все особенности модели общения с сервером с глаз пользователя. Не нашел.
В результате, после довольно краткого знакомства с «полем боя» задача плавно трансформировалась из «пишем бота» в «пишем средство для написания бота». Мне, в сущности, это было даже гораздо интереснее и ближе т.к., повторюсь, основной задачей было именно освоение питона, а не реализация конкретной задачи, но все же было бы интересно узнать, если такие решения существуют.
Итого: необходимо реализовать средство, которое бы позволяло писать ботов для телеграмма в привычной линейной схеме реализации алгоритма. Т.е. чтобы можно было писать что-то типа такого (питоно–подобный псевдокод):
Желаемый синтаксис логики бота
def logic( chat ): cfg = getSomeChatPersistentConfig() name = chat.user.name say( 'Hi, im a bot' ) if ( ask( 'I see you name as \nCan I use it talking with you?' ) ) cfg.name = name else while True: name = input('Please enter you name') if name: cfg.name = name else match choise('Your cancelled, what do you nant to do?'): case 'QUIT': say( 'Well, its sad, but hope you back soon.' return case 'NONEW': cfg.name = user.name say( 'Ok, let it be, ' ) break case 'RETRY': continue chat.clear() say( 'Lets start to work !' ) while True: match menu('What do you wana do?'): case 'QUIT': break case 'DEVICES': CallDeviceDisplayProc() case 'SETTINGS': CallSettingsProc() say('Bye, see you')
Т.е. решать задачу, а не воевать с ограничениями, накладываемыми на способ ее реализации.
Для решения этой задачи нам нужно сначала понять пару простых вещей:
- Логика: Какие интерфейсы нам нужны для реализации интерактивных задач.
- Функции бота: Какие способы взаимодействия с пользователем существуют, как и какие нужно использовать, чтобы реализовать нужное для нашей логики.
- Работа бота: Как и где эксплуатировать нашего бота. Как он будет получать управление.
- Запуск: Как запустить нашу логику параллельно «бесконечному циклу опроса сервера».
Работа бота
Я не буду вдаваться в различия способов эксплуатации бота и возможных мест откуда он может быть вызван, что и где можно или нельзя использовать. В конце концов у нас не бесконечность, а всего лишь одна неделя на то, чтобы ознакомиться с питоном и создать какой-то конечный продукт, поэтому сосредоточимся только на персональных чатах (тех, когда человек пишет чату явно, запуская его кнопкой «запуск»). По сходной же логике работают и боты, которые были добавлены в групповые каналы, но там есть свои особенности. Будем писать код, не обращая внимания на эти различия. По крайней мере пока.
Итого: пишем библиотеку для ботов, ориентированных на работу в персональном канале.
Функции бота
При работе телеграмма существуют следующие способы взаимодействия бот-человек:
- Написание текста как с одной, так и с другой стороны – это сообщение. В этом важно то, что бот ничего не делает до тех пор, пока ему не придет первое сообщение. Именно при получении сообщения определяется, откуда его написали (из какого канала), и кто это сделал. Т.к. события могут происходить абсолютно не связанно друг с другом, то сообщения будут приходить для разных каналов, от разных людей, которые находятся на разном этапе общения с ботом. Учитывая это нужно понимать что одна линейная бизнес-логика должна работать только для одного канала. Или так: для каждого канала должна быть запущена своя уникальная копия бизнес-логики. Сохранение как промежуточных, так и финальных данных происходит так же на «по–канальной» основе.
- В сообщении формируемом ботом, помимо «контента» можно использовать кнопки. Кнопки бывают двух видов: привязанные к сообщению и к чату (хотя тоже к сообщению, но зрительно… блин, тьфу на тебя АПИ телеграмма!). В детальное описание кнопок и их различий я вдаваться не буду по описанному уже выше принципу: «кому пофиг, а кто и так в курсе». В контексте статьи важен сам факт и различия в использовании этих кнопок. Кнопки у чата просто спамят текст в канал, а те, которые у сообщений, вызывают отправку специального обновления.
Бот может получить управление в любой момент, когда пользователь напишет какое-то сообщение или нажмет на кнопку. Это может случиться через секунду или через неделю. Не то, чтобы это было как-то важно для нашего недельного марафона, но учитывать все же стоит.
Нарисуем что-то типа:
class ISettings(typing.Sized): """Interface for ``Settings`` class""" _cfg: 'ISettings' def __init__(self,cfg:'ISettings'): self._cfg = cfg def gopt(self, path: str, default: TSettingsOption_t) -> TSettingsOption_t: """Get data from settings or set it to default if data not found""" return self._cfg.gopt(path,default) def sopt(self, path: str, value: TSettingsOption_t) -> TSettingsOption_t: """Set settings option to value """ return self._cfg.sopt(path,value) def sub_cfg(self, nm: str) ->'ISettings': """Get settings sub-key by full path in form of 'key.key'. """ return self._cfg.sub_cfg(nm)
class SettingsIStorage: """Interface for load\save storage""" def load(self, settings: ISettings): pass def save(self, settings: ISettings): pass
и забудем об этом вопросе.
Общение с ботом происходит в каждом канале независимо и уникально. Учитывая еще и тот факт, что боту может поступать сообщение с произвольными интервалами, то логика обработки сообщений должны быть такой: при получении сообщения находим канал, который его обслуживает. Если канала нет, то создаем новый. Передаем каналу сообщение. Если в канале уже работает логика, то распределяем сообщение в соответствии с ее потребностями, если ее нет, то запускаем ее, передав сообщение как стартовое. Код приводить не буду т.к. в реализации это все крайне просто, но очень много пробелов , «self.» и прочего не важного текста.
Желающие могут посмотреть реализацию самостоятельно.
Логика
Накидаем пример того, как нам хотелось бы, чтобы наша программа выглядела.
Пример рабочей программы
class Logic(ILogic): async def main(self, chat: BotChat, params: str) -> None: chat.user().name = chat.last.from_user.full_name name = chat.user().name if params: pstr = f'\nYou started me with parameters *""*, but I dont support any \n\n' else: pstr = '' titleMsg = await chat.reply( f'Hi, **.\n' f'' f'You are at examples section', media='data/Icon-Hi.png' ) while True: rc = await chat.menu( 'Choose test group to go', [[('➡ Menu tests. ', 'menu')], [('❓ Some asking', 'ask'), ('✌ Funny one :)', 'wait'), ('', 'calc')], [('❌ Close', 0), ('❌ Cancel', 0), ('❎ Abandon!', 0), ('➰ F* off!!', 0)], ], remove_unused=True ) if not rc.known: break if rc.data == 'menu': await logic_MENU(chat, name) elif rc.data == 'ask': await logic_ASK(chat, name) elif rc.data == 'wait': await logic_WAIT(chat, name) elif rc.data == 'calc': await logic_CALC(chat, name) else: break await titleMsg.delete() await chat.say(f'Calm down mate!\nIts all done already.\nSee you ', wait_delay=1) await chat.say(f'. btw, if you wanna reply you can use "/start" command.', wait_delay=2) await chat.say(f'Just saying. ')
На примере этого кода видно, что нам нужно реализовать:
- Передачу сообщений (в идеале с картинками).
- Удаление/Изменение сообщений
- Все вышеперечисленное, с учетом того, что нужны кнопки управления, которых бывает 2 вида.
- Реализовать «модальность» для сообщений, т.е. когда все события обрабатываются только одним элементом монопольно не давая выполняться обработчикам из предыдущих элементов до тех пор, пока модальный элемент не будет закрыт. Это нужно для реализации ввода, ответов на вопросы, меню и прочего.
- Реализовать возможность обрабатывать нажатия на кнопки в немодальных сообщениях.
Запуск
Любые библиотеки для работы ботов представляют из себя, в конце, концов такое:
executor.start_polling(dp)
Т.е. запуск некоего бесконечного цикла опроса и реакций на события, в то время как пользовательский код вызывается разными сортами такого:
@dp.channel_post_handler() async def post_handler(message: Message_t): … @dp.message_handler() async def message_handler(message: Message_t): … @dp.callback_query_handler() async def callback_handler(cbd: types.CallbackQuery): …
Т.е. наш код будет вызван при получении различных событий. Их есть некоторое разнообразие, но мы сосредоточимся на простейших: получение нового сообщения в канале и получения уведомления при нажатии кнопки.
Внедриться внутрь цикла с линейной логикой не представляется возможным, поэтому нужно запустить нашу программу параллельно ей и обеспечить взаимодействие из нее с данными, получаемыми «мелкими порциями» асинхронно из основного цикла.
Мне, как человеку далекому от птичьих языков более привычным был бы способ создания отдельного процессорного потока для выполнения логики, но раз подвернулась такая оказия, то заодно разберемся с co-routines (я без понятия как это называется по русски), в windows эти сущности называются Fibers , где-то еще могут быть другие названия.
Суть подхода в том, что множество логически не связанного кода выполняется в одном потоке, передавая друг другу управления в тот момент, когда этому коду нечего делать.
Мы пишем http клиента и «нечего делать» — это его основное занятие, поэтому в топку «взрослые» потоки, пишем на «резиновых женщинах».
Добавить свой код в очередь выполнения очень просто:
async def _wrapper(): self.log.error(f'Start bot logic task') try: await self.logic.main(self, params if params else '') self.logicTask = None … self.logicTask = asyncio.get_event_loop().create_task(_wrapper())
где logicTask хранит объект добавленного кода, а self.logic.main запускает на выполнение процедуру с реализацией линейной логики. С учетом разных нужд пусть интерфейс для нашей логики будет выглядеть так:
Код интерфейса логики
class ILogic(ISettings): """Interface for linear logic implementation for each ``BotChat`` channel. Each ``BotChat`` object uses this interface to call user logic functions. Each channel will have separate, unique logic object. """ chat: 'BotChat' def __init__(self, chat: 'BotChat', cfg: ISettings): super().__init__(cfg) self.session = chat async def main(self,chat:'BotChat',params:typing.List[str]) -> None: """Main user logic function. Called after '/start' or '/restart' command and will run until exception happen or finished. Executed in parallel with bot task. Note: MUST NOT block on operations called from outside (like callbacks). :param chat: parent chat executing logic :param params: string with parameters passed to '/start' or '/restart' commands. """ pass def OnExit(self,chat:'BotChat',isAlive:bool) -> None: """Called after logic procedure finished. Used just for notification. Can be used f.i. to free resources. :param chat: parent chat executing logic :param isAlive: True is chat object is alive (can be used to communicate with channel), False if chat is closed. """ pass # ret false to disable bot_ilogic restart async def OnDownDecide(self, chat: 'BotChat', message: Message_t) -> bool: """Called to decide what to do if some notifications received in channel but logic is down (finished or terminated by error) :param chat: parent chat executing logic :param message: message object which "wake up" channel :return: True to restart logic task or False to stay dead. """ return True
Пользователь нашей библиотеки будет наследоваться от этого интерфейса и реализовывать то, что ему нужно. По крайней мере main .
Код
Разбираемся с АПИ телеграмма (в синтаксисе выбранной библиотеки) и выясняем что все кроме модальности реализуемо довольно просто:
Создаем новые сообщения (тип зависит от того с картинкой оно нужно или нет) со всеми возможными наворотами:
async def _createMessage(self) -> MessageId_t: reply_to_message_id = self.reply_to_message_id if not reply_to_message_id: reply_to_message_id = None if self.media: msg = await self.chat.bot.send_photo( self.chat.chat_id, parse_mode=self.chat.bot.parse_mode, photo=self._loadMedia(self.media), caption=self.chat.escape_soft(self.text), reply_markup=self.keyboard.markup, reply_to_message_id=reply_to_message_id) else: msg = await self.chat.bot.send_message( self.chat.chat_id, text=self.chat.escape_soft(self.text), reply_markup=self.keyboard.markup, reply_to_message_id=reply_to_message_id) return msg.message_id
Редактирование существующего сообщения будет чуть сложнее (спасибо такому однозначному и «единообразному» протоколу телеграмма), но тоже ничего выдающегося:
async def _updateMessage(self) -> None: if self.media: if self._media.changed: await self.chat.bot.edit_message_media( media=types.InputMedia( type='photo', media=self._loadMedia(self.media), caption=self.chat.escape_soft(self.text) ), chat_id=self.chat.chat_id, message_id=self.message_id, reply_markup=self.keyboard.markup) elif self._text.changed: await self.chat.bot.edit_message_caption( chat_id=self.chat.chat_id, message_id=self.message_id, caption=self.chat.escape_soft(self.text), reply_markup=self.keyboard.markup ) else: if self._text.changed: await self.chat.bot.edit_message_text( text=self.chat.escape_soft(self.text), chat_id=self.chat.chat_id, message_id=self.message_id, reply_markup=self.keyboard.markup ) elif self.keyboard.changed: try: await self.chat.bot.edit_message_reply_markup( chat_id=self.chat.chat_id, message_id=self.message_id, reply_markup=self.keyboard.markup ) # just mask unchanged error instead complex keyboard comparison except aiogram.utils.exceptions.MessageNotModified: pass
Это все, разумеется, надо обернуть кучей логики и проверок на то, в каких условиях и с какими «бубнами» вызывать то или другое. Желающие могут посмотреть реализацию самостоятельно.
Удаление сообщений существует и, слава богу, простейшее без каких-либо ветвлений в истории развития протокола:
try: if await self.bot.delete_message(chat_id=self.chat_id, message_id=message_id): return True except BadRequest as e: return False
Кнопки в протоколе описываются по разному, но пофик, унифицируем все до:
BotUserKey_t = typing.Union[typing.Tuple[str, typing.Any], str]
для любой клавиатуры. Это лишняя страница кода, зато не нужно париться с типами при использовании. Стоит того.
Модальность к самому телеграмму отношения уже не имеет — это чисто логический наворот, который реализуем простой очередью сущностей, которые ждут получения событий. Т.к. событий всего два, то и интерфейс для такой сущности будет иметь всего две полезные функции. Так же эти «ждуны» должны уметь быть посредниками между кодом логики, который будет ожидать каких-то событий и получаемыми мелкими событиями. Принцип простой: с одной стороны, если логике что-то надо, то она заводит «ждуна» и ждет, пока поставленное условие не выполнится. С другой стороны, при получении мелких событий от телеграмма, проверяется очередь «ждунов» и, начиная с последнего, полученные данные применяются до тех пор, пока не дойдем до модального «ждуна» или до такого, который ровно этого события и ждал. Реализуется почти так же просто как и звучит.
Интерфейс ждуна, грубо говоря, такой:
Интерфейс класса ожидания
class Waiter: """Class which is used to filter received messages and callbacks and pass execution to user logic. """ chat: 'BotChat' isModal: bool = False def __init__(self, chat: 'BotChat', messge_id: MessageId_t, /, on_message: typing.Optional[OnMessageEvent] = None, on_callback: typing.Optional[OnCallbackEvent] = None): """ Create base waiter class :param chat: parent chat waiter will be attached to :param messge_id: message id this waiter attacked to if applicable (NoMessageId if waiter not attached to single message) :param on_message: Callback to call on new messages :param on_callback: Callback called on new INLINE buttons data """ self.chat = chat self.messge_id = messge_id self._completed = asyncio.Event() self._completed.clear() self._on_message = on_message self._on_callback = on_callback async def isWaitingThisMessage(self, chat: 'BotChat', message: Message_t) -> bool: """Check if this waiter process specified message""" if self._on_message and await self._on_message(self.chat, message): return True return False async def isWaitingThisCallback(self, chat: 'BotChat', cbd: Callback_t) -> bool: """Check if this waiter process specified callback data""" if self._on_callback and await self._on_callback(self.chat, cbd): return True return False def notify_complete(self): """Used to notify waiting user logic, what wait is complete. Called from bot loop to inform user logic""" self._completed.set() async def wait(self, timeout: float = None) -> bool: """Wait until complete. Called from user logic to wait waiter condition.""" if timeout and timeout >= 0: try: await asyncio.wait_for(self._completed.wait(), timeout) return True except asyncio.TimeoutError: self.chat.waiterRemove(self) return False else: await self._completed.wait() return True
Ну и в классе нужно не забыть создать для него небольшую инфраструктуру:
waitersLock: threading.RLock waiters: typing.List[typing.Optional[Waiter]]
В этом месте есть небольшой момент, на который стоит обратить внимание. Локер, на котором синхронизируется список «ждунов» должен быть: а) реентерабельным и б) не отпускающим очередь «детских потоков».
Первое понятно для чего, а вот второе важно в единственном моменте. В телеграмме, при получении события от нажатой кнопки на это событие можно ответить и при ответе у пользователя всплывет либо модальное окошко с текстом, либо пропадающий хинт. Так вот, если использовать блокировки из набора асинхронной библиотеки, то в момент блокировки будет передано управление первому попавшему ждущему своей очереди коду. Если окажется так, что этот код отправит серверу телеграмм, какие-то данные , то ответ на событие уже можно и не отправлять – оно не сработает, пользователь не увидит реакции.
Если не хочется лишать себя такой функции как эти «быстрые ответы прям сразу при нажатии», то асинхронные локеры использовать нельзя. В примере выше используется многопоточный реентерабельный локер, хотя учитывая структуру самого приложения его функция чисто декоративная и не более интеллектуальная чем:
While not stateVariable: pass
только что использовать удобнее и писать меньше.
По функционалу вроде бы все.
Итог
Если не лукавить, то времени ушло чуть более недели. Впрочем, меня извиняет поездка на шашлыки и пара катаний на дачу.
Зачем я все это тут описал?
Целей у меня, на самом деле, несколько.
Во-первых, хотелось бы узнать мнение тех, кто пишет на питоне давно и серьезно. Все же этот код я писал как знакомство я языком и буду рад любым отзывам о его качестве. Однако просьба не забывать, что 2 недели назад я путал питона с ужом 🙂
Во-вторых, мне интересно было бы узнать о подобных альтернативах. Все же я не верю, что такое никто не реализовывал ранее.
Ну и, в третьих, на тот случай, если мой код или его описание окажется для кого-то в чем-то полезным. Буду рад.
Код, описанием и историей которого является эта статья доступен по ссылке.
