Найти в Дзене
Записки сисадмина

Python. Telegram bot. Отправляем и принимаем разные типы собщений.

Продолжаем разбираться с API telegram. Ранее мы уже написали простейшего бота, который умеет обрабатывать 2 команды. Но там же мы упомянули, что бот еще может реагировать на разные типы данных (content_types) в сообщениях. print(telebot.util.content_type_media) ['text', 'animation', 'audio', 'document', 'photo', 'sticker', 'story', 'video', 'video_note', 'voice', 'contact', 'dice', 'game', 'poll', 'venue', 'location', 'invoice', 'successful_payment', 'connected_website', 'passport_data', 'web_app_data'] Здесь нас пока что интересуют только некоторые типы: text - Обычное текстовое сообщение. Если к нему будет прикреплен любой файл, сообщение изменит свой тип. animation - GIF картинка. audio - Аудио, которое можно проиграть в телеграм (отправленное не файлом), но не голосовое сообщение document - Любой документ, который не подходит по критериям остальных типов: не картинка, не гифка, не видео и т.д. Обычно, это архив, текстовый файл, или Excel таблица. photo - Картинка. sticker - Стик
Оглавление

Продолжаем разбираться с API telegram. Ранее мы уже написали простейшего бота, который умеет обрабатывать 2 команды. Но там же мы упомянули, что бот еще может реагировать на разные типы данных (content_types) в сообщениях.

Входящие сообщения

  • Давайте посмотрим, что вообще наш бот физически может обработать:
print(telebot.util.content_type_media)
['text', 'animation', 'audio', 'document', 'photo', 'sticker', 'story', 'video', 'video_note', 'voice', 'contact', 'dice', 'game', 'poll', 'venue', 'location', 'invoice', 'successful_payment', 'connected_website', 'passport_data', 'web_app_data']

Здесь нас пока что интересуют только некоторые типы:

text - Обычное текстовое сообщение. Если к нему будет прикреплен любой файл, сообщение изменит свой тип.

animation - GIF картинка.

audio - Аудио, которое можно проиграть в телеграм (отправленное не файлом), но не голосовое сообщение

document - Любой документ, который не подходит по критериям остальных типов: не картинка, не гифка, не видео и т.д. Обычно, это архив, текстовый файл, или Excel таблица.

photo - Картинка.

sticker - Стикер.

video - Видео.

video_note - Кружочек.

voice - Голосовое сообщение.

  • Давайте напишем простой обработчик текстовых сообщений.
-2
@bot.message_handler(content_types=['text'])
def text_message(message):
bot.reply_to(message, 'Я обработал текстовое сообщение')

Здесь мы явно указали, что наш декоратор получит каждое текстовое сообщение. А также мы использовали новый метод бота: reply_to, который не просто отправляет сообщение в чат, а именно отвечает на конкретное сообщение пользователя.

-3
  • А если мы хотим, чтобы разные текстовые сообщения обрабатывались по-разному?

Текст сообщения находится в поле message.text, по которому нам нужно будет запускать проверки.

Тут у самурая есть несколько путей:

1. Принимать все тестовые сообщения в одну функцию и разбирать их по условиям там:

-4
@bot.message_handler(content_types=['text'])
def text_message(message):
if 'привет' in message.text.lower():
bot.reply_to(message, 'Рад видеть тебя')
elif 'помощь' in message.text.lower():
bot.reply_to(message, 'Как я могу тебе помочь?')
else:
bot.reply_to(message, 'Я обработал текстовое сообщение')

2. Использовать функции самого телеграмма, передавая в message_handler функцию вместо content_types:

-5
@bot.message_handler(func=lambda message: 'привет' in message.text)
def choosen_message(message):
bot.reply_to(message, 'Рад видеть тебя')
@bot.message_handler(func=lambda message: 'помощь' in message.text)
def choosen_message(message):
bot.reply_to(message, 'Как я могу тебе помочь?')
@bot.message_handler(content_types=['text'])
def text_message(message):
bot.reply_to(message, 'Я обработал текстовое сообщение')
-6

Важно: во втором случае message_handler с content_types='text' нужно помещать самым последним, т.к. телеграм бот - это скрипт, и он будет проверять функции по очереди сверху вниз.

  • А если мы хотим наоборот объединить разные типы сообщений в один обработчик?

Тоже можно. Для этого в content_types прописываем необходимые типы через запятую:

-7
@bot.message_handler(content_types=['photo', 'document', 'video'])
def combined_message(message):
bot.send_message(message.chat.id, 'Я получил сообщение с файлом')
-8

Важно: если мы получили текстовое сообщение, весь текст этого сообщения будет находиться в message.text, но в случае, когда мы получаем любой файл (фото, документ и т.д.) с текстом внизу, этот текст будет находиться в поле message.caption (подпись).

Итак, мы научились принимать различные типы сообщений. Пора учить нашего бота отправлять их.

Исходящие сообщения

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

  • Текстовые сообщения:

Метод send_message(), которым мы уже ни раз пользовались. Самый простой метод, много о нем расписывать смысла нет. Есть только несколько интересных фишек, которые, впрочем, доступны и в остальных методах:

disable_notification=True/False - Отправка сообщения без звука (без уведомления).

timeout=число - Отложенное сообщение, отправить через указанное количество секунд.

protect_content=True/False - Запрет копирования и пересылки сообщения.

  • Сообщения с файлом:

Каждому типу файлов соответствует свой метод. Так для отправки картинки нужно использовать метод send_photo, для документа - send_document и так далее. Единственное их различие в том, какой именно тип файлов мы помещаем внутрь сообщения.

  • Давайте разбираться на примере метода send_photo().

Создадим команду /kartinka, при обращении к которой, бот будет отправлять нам фото. Сразу же мы сталкиваемся с вопросом: как нам передать картинку в метод? С текстом было просто, мы просто отдавали строку и она становилась текстом нашего сообщения.

Ответ прост: мы должны в поле photo передать бинарный поток открытой картинки. Звучит в теории сложно, на практике проще:

В директории нашего бота создадим файл blank_user.jpg

-9
@bot.message_handler(commands=['kartinka'])
def help_message(message):
with open('blank_user.jpg', 'rb') as photo:
bot.send_photo(message.chat.id, photo)

1. Мы используем конструкцию "with open", чтобы после отправки, файл закрылся сам, не забивая нам оперативную память.

2. 'rb' означает read bynary - чтение в бинарном формате.

Весь бинарный поток мы помещаем в переменную photo и передаем ее в метод send_photo().

-10
  • А как отправить картинку с подписью?

Добавляем нашему методу параметр caption, о котором мы уже говорили.

-11
@bot.message_handler(commands=['kartinka'])
def help_message(message):
with open('blank_user.jpg', 'rb') as photo:
bot.send_photo(chat_id=message.chat.id, photo=photo, caption='Моя картинка')
-12

Методы send_video(), send_audio(), send_document() работают аналогично.

  • Сообщения с несколькими файлами:

Когда нам необходимо отправить несколько файлов в одном сообщении, нам на помощь приходит метод send_media_group.

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

Вместо текста, или документа, мы должны передать в метод media: List[InputMediaAudio | InputMediaDocument | InputMediaPhoto | InputMediaVideo]

Соответственно, для начала нам нужно импортировать нужные модули в наш скрипт:

from telebot.types import InputMediaPhoto, InputMediaAudio, InputMediaDocument, InputMediaVideo

  • Попробуем отправить несколько картинок в одном сообщении:

Создаем в нашей директории файлы 'blank_user.jpg', 'blank_user1.jpg', 'blank_user2.jpg'.

-13
@bot.message_handler(commands=['kartinki'])
def help_message(message):
file_list = ['blank_user.jpg', 'blank_user1.jpg', 'blank_user2.jpg']
input_files = []
for file in file_list:
input_files.append(InputMediaPhoto(open(file, 'rb')))
bot.send_media_group(chat_id=message.chat.id, media=input_files)

Мы помним, как при отправке одной картинки, мы в метод send_photo() передавали бинарно открытый файл. В данном случае мы должны передать список, который состоит из InputMediaPhoto(открытый файл).

-14

К сожалению, красиво код написать не получится. Особенно, если мы хотим добавить текст к нашему сообщению. Как мы помним, текст добавляется в поле caption. Однако, если мы добавим подпись каждому файлу, ничего не произойдет.

-15

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

Caption добавляется только первой картинке.

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

-16
@bot.message_handler(commands=['kartinki'])
def help_message(message):
file_list = ['blank_user1.jpg', 'blank_user2.jpg']
input_files = [InputMediaPhoto(open('blank_user.jpg', 'rb'), caption='Групповое сообщение')]
for file in file_list:
input_files.append(InputMediaPhoto(open(file, 'rb')))
bot.send_media_group(chat_id=message.chat.id, media=input_files)
-17

Все работает, сообщения отправляются. В следующих постах будем продолжать развивать функционал нашего бота.

Весь код выложил здесь