Продолжаем разбираться с 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 - Голосовое сообщение.
- Давайте напишем простой обработчик текстовых сообщений.
@bot.message_handler(content_types=['text'])
def text_message(message):
bot.reply_to(message, 'Я обработал текстовое сообщение')
Здесь мы явно указали, что наш декоратор получит каждое текстовое сообщение. А также мы использовали новый метод бота: reply_to, который не просто отправляет сообщение в чат, а именно отвечает на конкретное сообщение пользователя.
- А если мы хотим, чтобы разные текстовые сообщения обрабатывались по-разному?
Текст сообщения находится в поле message.text, по которому нам нужно будет запускать проверки.
Тут у самурая есть несколько путей:
1. Принимать все тестовые сообщения в одну функцию и разбирать их по условиям там:
@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:
@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, 'Я обработал текстовое сообщение')
Важно: во втором случае message_handler с content_types='text' нужно помещать самым последним, т.к. телеграм бот - это скрипт, и он будет проверять функции по очереди сверху вниз.
- А если мы хотим наоборот объединить разные типы сообщений в один обработчик?
Тоже можно. Для этого в content_types прописываем необходимые типы через запятую:
@bot.message_handler(content_types=['photo', 'document', 'video'])
def combined_message(message):
bot.send_message(message.chat.id, 'Я получил сообщение с файлом')
Важно: если мы получили текстовое сообщение, весь текст этого сообщения будет находиться в message.text, но в случае, когда мы получаем любой файл (фото, документ и т.д.) с текстом внизу, этот текст будет находиться в поле message.caption (подпись).
Итак, мы научились принимать различные типы сообщений. Пора учить нашего бота отправлять их.
Исходящие сообщения
Как и в обычном диалоге с человеком, приятно, когда на твое сообщение отвечают. Сегодня мы разберем два типа отправляемых сообщений: текстовые и с вложениями.
- Текстовые сообщения:
Метод send_message(), которым мы уже ни раз пользовались. Самый простой метод, много о нем расписывать смысла нет. Есть только несколько интересных фишек, которые, впрочем, доступны и в остальных методах:
disable_notification=True/False - Отправка сообщения без звука (без уведомления).
timeout=число - Отложенное сообщение, отправить через указанное количество секунд.
protect_content=True/False - Запрет копирования и пересылки сообщения.
- Сообщения с файлом:
Каждому типу файлов соответствует свой метод. Так для отправки картинки нужно использовать метод send_photo, для документа - send_document и так далее. Единственное их различие в том, какой именно тип файлов мы помещаем внутрь сообщения.
Создадим команду /kartinka, при обращении к которой, бот будет отправлять нам фото. Сразу же мы сталкиваемся с вопросом: как нам передать картинку в метод? С текстом было просто, мы просто отдавали строку и она становилась текстом нашего сообщения.
Ответ прост: мы должны в поле photo передать бинарный поток открытой картинки. Звучит в теории сложно, на практике проще:
В директории нашего бота создадим файл blank_user.jpg
@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().
- А как отправить картинку с подписью?
Добавляем нашему методу параметр caption, о котором мы уже говорили.
@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='Моя картинка')
Методы 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'.
@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(открытый файл).
К сожалению, красиво код написать не получится. Особенно, если мы хотим добавить текст к нашему сообщению. Как мы помним, текст добавляется в поле caption. Однако, если мы добавим подпись каждому файлу, ничего не произойдет.
В данном примере подпись не прогрузится и мы от бота получим все те же 3 картинки в сообщении.
Caption добавляется только первой картинке.
Давайте немного извернемся и попытаемся оптимизировать наш код. Вынесем нашу первую фотографию сразу в список отправляемых файлов и добавим к ней подпись:
@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)
Все работает, сообщения отправляются. В следующих постах будем продолжать развивать функционал нашего бота.