Bot API v2: Специальные кнопки, опять редактирование сообщений, кэшированный инлайн
Продолжаю рассказывать о нововведениях в Bot API версии 2. Мы не будем рассматривать методы getChat, getChatMember и т.д., которые появились в обновлении 2.1: они интуитивно понятны и особых проблем не вызывают. Вопросы могут возникнуть при изучении специальных обычных кнопок, вроде тех, что запрашивают у вас номер телефона и геолокацию, при попытке получить отредактированное сообщение, а также при работе с уже загруженными в облако объектами с инлайн-режимом. Обо всём по порядку.
Специальные кнопки
Некоторым ботам жизненно необходим ваш номер телефона или местоположение, например, для привязки к учётным записям на других сайтах или же поиска близлежащих объектов на карте. Разработчики Telegram прислушались к мнению ботоводов и добавили особые свойства обычным (не инлайновым) кнопкам. Итак, чтобы запросить номер телефона, нужно помимо аргумента text
передать аргумент request_contact=True
, а для геолокации, соответственно, request_location=True
. Обратите внимание, что одновременно у кнопки может быть не больше одного особого свойства (можно не указывать никакой), а также что специальные кнопки могут быть отправлены только в диалоги (бот-человек). Напишем код, который на команду /geophone отправит нам клавиатуру с этими кнопками.
# не забудьте про from telebot import types
@bot.message_handler(commands=["geophone"])
def geophone(message):
# Эти параметры для клавиатуры необязательны, просто для удобства
keyboard = types.ReplyKeyboardMarkup(row_width=1, resize_keyboard=True)
button_phone = types.KeyboardButton(text="Отправить номер телефона", request_contact=True)
button_geo = types.KeyboardButton(text="Отправить местоположение", request_location=True)
keyboard.add(button_phone, button_geo)
bot.send_message(message.chat.id, "Отправь мне свой номер телефона или поделись местоположением, жалкий человечишка!", reply_markup=keyboard)
При нажатии на кнопку отправки номера телефона сервер вернёт объект Message с непустым типом Contact, а при нажатии на кнопку отправки геолокации – с непустым типом Location.
Важно: если вы используете конечные автоматы или любой другой механизм состояний, который будет ждать от пользователя его телефон в объекте Contact, помните, что ушлый юзер может попробовать обмануть бота и скинуть любой другой контакт из записной книжки. Чтобы убедиться, что номер телефона принадлежит именно этому конкретному пользователю, сравните user_id
в объекте from
с user_id
в объекте Contact
, они должны совпадать.
Редактирование сообщений пользователями
Начиная с мая 2016 года, пользователи могут редактировать свои сообщения, а боты могут видеть исправления. Как им в этом помочь, давайте разберёмся вместе. В качестве примера заставим нашего бота отвечать на ругательства. К примеру, если пользователь пишет “дурак”, бот ответит “сам дурак”. Хитрые люди могут попробовать отредактировать своё сообщение и выставить бота в дурном свете, но мы будем изменять ответ бота под пользовательский текст.
Для отслеживания изменений, у нас в копилке появился новый тип хэндлеров – edited_message_handler
, который настраивается точно так же, как и message_handler
, просто “ловит” он только те сообщения, которые отредактированы. Что ж, ничего сложного, пишем!
@bot.message_handler(func=lambda message: True)
def any_message(message):
bot.reply_to(message, "Сам {!s}".format(message.text))
@bot.edited_message_handler(func=lambda message: True)
def edit_message(message):
bot.edit_message_text(chat_id=message.chat.id,
text= "Сам {!s}".format(message.text),
message_id=message.message_id + 1)
Заметили? Да, при вызове edit_message_text
надо указать message_id
на единицу бОльший, чем тот, который прислан сервером, потому что сервер сообщает о сообщении от пользователя, а нам нужно редактировать сообщение бота, которое шло за ним следом. И вот как это будет выглядеть (это одни и те же сообщения, что видно по метке “изм.” около моего)
Кэшированный инлайн
Когда инлайн-боты только появились, то в качестве источника данных для ответов надо было указывать внешние ссылки, причем с ограничениями по размеру указываемого файла. Очевидно, такой подход мог быть не очень быстрым, а чем дольше пользователь ждёт, тем он менее доволен результатами работы бота :) В итоге, в Bot API v2 инлайн-режиму разрешили в качестве источника для медиа использовать file_id
уже имеющихся на сервере файлов (напомню, что file_id
для одного и того же файла будут разниться от бота к боту) Итак, у меня есть file_id
двух фотографий с капибарами (как получить file_id
загружаемых боту фотографий, считайте это заданием для самоподготовки), надо на любой инлайн-запрос (даже пустой) предложить эти 2 изображения. По сути, всё сводится к замене типа InlineQueryResultPhoto
на тип InlineQueryResultCachedPhoto
@bot.inline_handler(func=lambda query: True)
def inline_mode(query):
capibara1 = types.InlineQueryResultCachedPhoto(
id="1",
photo_file_id="AgADAgAD6rMxGyBnGwABgBmcoHgy01IENAAQSYK_1gyoAAU-5aQACAg",
caption="Это капибара №1"
)
capibara2 = types.InlineQueryResultCachedPhoto(
id="2",
photo_file_id="AgADAgAD67MxGyBnGwABCvqPIYxMoNHENAAS51HjO88y_Z0ffAQABAg",
caption="Это капибара №2"
)
bot.answer_inline_query(query.id, [capibara1, capibara2])
Запускаем бота. Ура, теперь мы умеем очень быстро предлагать разных капибар нашим пользователям :)
Помимо фотографий, из “кэша” можно показывать любые типы, поддерживаемые мессенджером: видео, аудио, стикеры, файлы (пока что только pdf
и zip
).
На этом всё. Хороших ботов!