Telegram bot описание — Урок 7. Встраиваемые боты (Inline)
Урок 7. Встраиваемые боты (Inline)
Важно
: код, приведённый здесь, актуален для первой версии Bot API. Пожалуйста, откройте урок 8, чтобы узнать о важных изменениях.
4 января 2016 года разработчики Telegram выпустили большое дополнение к существующему BotAPI, заключающееся в появлении встраиваемых ботов.К сожалению, не все до конца понимают, в чем их особенность. А вот в чем: если раньше для того, чтобы получить какую-либо информацию от бота и перекинуть её собеседнику нужно было открывать диалог с ботом, писать всякие команды, а потом пересылать ответ в нужный чат, то теперь всё стало быстрее и проще (для некоторых ситуаций): просто открываете нужный чат, вызываете бота, введя его ник в поле ввода сообщения, ставите пробел и пишете свой запрос. Бот отвечает на эти запросы в виде всплывающих подсказок, число и содержание которых зависит от того, что вы написали боту и от заложенного в него алгоритма. Если одна из подсказок удовлетворяет вашему запросу, нажимаете на неё и некоторое сообщение отправляется в тот чат, в котором вы находитесь.Например, если вы хотите отправить своему собеседнику ссылку на статью о Telegram из русскоязычной Википедии, то достаточно ввести в поле ввода @wiki ru Telegram
и подождать пару секунд. Результат представлен на рисунке:

Соответственно, если вы нажмете на одну из этих подсказок, то ссылка на соответствующую статью будет отправлена в текущий чат. Собственно, всё!
Помимо ссылок, при помощи Inline API можно отправлять фотографии, GIF-анимации, видеоанимации в формате Mpeg-4 и ссылки на видеозаписи, которые можно будет смотреть прямо в приложении.
Прежде, чем мы начнём писать своего встраиваемого бота, несколько слов о том, для чего НЕ надо их использовать и какие есть ограничения. Во-первых, такие боты нужны именно для подсказок, когда вы хотите чем-то поделиться, но вам нужна помощь бота. Вы же не помните все ссылки на те самые видео с котиками? А бот быстренько поможет его найти, чтобы вы поделились им в чатике. Во-вторых, существуют недокументированные ограничения по объемам отправляемой информации. Скажем, для текстов и ссылок нельзя отправлять элементы длиннее 300-400 символов (на самом деле, получается без проблем около двухсот, больше — с оговорками). На мой взгляд, это самый неприятный момент в API. Я давеча пытался прикрутить поиск цитат с сайта Bash.im из своей базы для одного из моих ботов. Увы, всё, чего удалось добиться, это отправка коротких цитат (примерно с твит размером) непосредственно текстом, длинных — через отправку ссылок на сам Баш.
Ну а теперь приступим к созданию нашего первого встраиваемого бота. Вообще говоря, никто не мешает добавить существующему Inline-возможности, но дабы не усложнять код, напишем отдельного.
Что же он будет уметь? Пусть на вход боту будут подаваться 2 числа, а он в подсказках будет предлагать основные математические действия: сложение, вычитание, умножение и деление. Да, пример надуманный, но как нельзя лучше показывает особенности Inline API.
В начале, зарегистрируем бота у @BotFather (или воспользуемся имеющимся) для него вызовем команду /setinline
.Внимание: после ввода этой команды и выбора бота нужно ввести подсказку, появляющуюся в поле ввода при вызове нашего бота в дальнейшем, иначе фокус не удастся.
Создадим файл исходного кода и добавим туда необходимые импорты и объект самого бота:
Мы условились, что на вход будут подаваться 2 числа, поэтому нам нужно построить регулярное выражение, которое будет реагировать на корректный ввод. Оно очень простое: digits_pattern = re.compile(r'^[0-9]+ [0-9]+$', re.MULTILINE)
.
А теперь самое главное — логика. Пока юзер пишет, Телеграм никак себя не проявляет, но как только он делает паузу, приложение считает, что это законченный ввод и отправляет его боту. Юзер дописал ещё пару символов и остановился? Окей, снова кидаем это боту. Соответственно, может сложиться ситуация, когда пользователь ввёл одно число и задумался. Наш парсер, попробовав сопоставить текст на наличие регулярного выражение, ругнётся и сотворит всякие нехорошие вещи, например, остановит весь скрипт (мы же помним, что Python — интерпретируемый язык, да?). Значит, надо ставить ловлю исключений:
Если условие выше отработало без ошибок, значит, мы получили два искомых числа. Теперь начинаются особенности Inline API. Каждая подсказка, присылаемая ботом, это объект с набором некоторых характеристик. Мы будем использовать объект типа «Статья«.Согласно документации, обязательных параметров у такого объекта четыре: тип, уникальный идентификатор, заголовок и текст, который будет отправлен. По поводу типа можно не беспокоиться: используемая мной уже больше полугода библиотека pyTelegramBotAPI подставит нужное значение сама. Уникальный идентификатор придется ставить самим и тут есть подвох: при использовании подгрузки (об этом в конце урока) идентификаторы должны быть уникальными у всех значений в одном большом запросе (т.е. у исходных результатов и всех подгружаемых в пределах одного запроса). Учтите это. Заголовок — очевидно. Отправляемый текст — то, что будет отправлено в текущий чат при нажатии на данную подсказку.Есть ещё необязательный параметр «описание» (description). Так вот, описание != отправляемый текст. В подсказке может быть написано «Наполеон — это торт», а в отправляемом тексте могут содержаться коды запуска ядерный ракет. Шутка, но смысл понятен.
Вооружившись этими знаниями и имея на руках два присланных пользователем числа, давайте создадим 4 объекта, каждый из которых отвечает за свою математическую операцию (на самом деле 5, т.к. учтем деление на ноль):
Полагаю, тут ничего сверхсложного нет. Вызывает интерес только последняя строка, а именно метод answer_inline_query
. У него обязательных параметров два: уникальный идентификатор запроса (его получает хэндлер, так что про него забываем) и массив ответов. Загоняем теперь предыдущие два куска кода в одну функцию, обернутую хэндлером:
И можно запускать! Введём в любом чате, кроме секретного, ник нашего бота и два числа. Смотрим на подсказку:

В принципе, правильно, но как-то не наглядно. Да и не очень понятно для нового пользователя, как, что и зачем.А давайте учтем ситуацию, когда пользователь ввёл только ник бота, и покажем ему подсказку:
Уже лучше, но всё равно не очень наглядно. А давайте добавим превью для каждой подсказки, чтобы слева была иконка соответствующей операции, и, заодно, сделаем превью при делении на ноль кликабельным, отправляя пользователя на эту страницу Википедии.Во-первых, закачаем на какой-нибудь бесплатный хостинг иконки с нашими математическими операциями (сгенерировал в Metro Studio, размер каждой 48×48 px):
Во-вторых, отредактируем функцию query_text
, добавив к каждому объекту типа «Статья» иконку, а для деления на ноль — свою иконку и ссылку:
Обратите снова внимание на функцию answer_inline_query
, в ней я добавил аргумент cache_time
. Значит он именно то, что первым приходит в голову — устанавливает время, на которое результат будет закэширован, чтобы лишний раз не дергать бота. Сейчас на моей стороне тот факт, что математика — фундаментальная наука, и за те 68 с копейками лет, что указаны у меня в аргументе cache_time
, результат операций не изменится. Но! Выше на рис. 2 у меня слева от подсказок нет ничего, никаких картинок. И да, в течение 68 с лишним лет конкретно для данного запроса так и будет. Поэтому трижды подумайте, прежде чем установите большое время кэширования. Для каких-то динамических результатов, например, для подсказки при нулевом запросе, вполне достаточно времени 86400
(1 сутки). По умолчанию, кстати, вообще всего 300 секунд, т.е. 5 минут.
Теперь наши результаты выглядят куда симпатичнее:


Прекрасно! Мы написали своего первого встраиваемого бота! Но…Но есть ещё такая штука, как offset
. Вообще говоря, это нужно для постепенной подгрузки новых результатов. Например, пользователь запрашивает видео с котиками, бот возвращает пять штук. Как только юзер долистывает до конца, бот запрашивает у своего источника ещё пять, а Telegram дописывает их к уже имеющимся. В итоге, уже 10 вариантов и так далее. В одном из своих ботов я сделал так: запрашиваю из БД 5 записей с offset
равным нулю. При отправке их пользователю, я устанавливаю в функции answer_inline_query
параметр next_offset
в значение 5
(строковый параметр, если что). Если юзер долистал до конца, то боту придет запрос с этим же текстом, но с значением offset
как раз 5, поэтому в следующий раз я из БД запрошу уже следующие пять записей, прибавлю ещё пятерку к текущему значению, получая 10
и снова верну пользователю значения. Как только у меня закончились соответствующие запросу данные в базе, отправляю пустую строку в качестве аргумента next_offset
. В общем, всё выглядит примерно так (бОльшая часть кода вырезана для упрощения понимания):