The English translation of this guide is coming soon. For now the text is in Russian — switch to RU or keep reading below. Open Russian version

Advanced

Inline mode

Inline mode позволяет пользователям вызывать бота в любом чате через @username запрос. Бот получает inline_query, формирует список результатов и возвращает их через answerInlineQuery. Пользователь выбирает результат — он вставляется в поле ввода как сообщение от его имени.

Включение Inline mode

В BotFather выберите бота → Bot SettingsInline ModeTurn on. Можно задать placeholder (подсказку в поле ввода). После включения бот начнет получать апдейты типа inline_query.

Структура inline_query

Апдейт содержит:

  • id — уникальный идентификатор запроса (string, до 64 байт)
  • from — пользователь, сделавший запрос
  • query — текст запроса (может быть пустым)
  • offset — смещение для пагинации (string)
  • chat_type — тип чата, из которого сделан запрос (optional)
  • location — местоположение пользователя (optional, если запрошено)

Ответ: answerInlineQuery

Метод принимает массив results (до 50 элементов) и опциональные параметры:

  • cache_time — кэширование результата на стороне клиента (сек, дефолт 300)
  • is_personal — результаты персональны для пользователя
  • next_offset — токен для следующей страницы (пагинация)
  • switch_pm_text / switch_pm_parameter — кнопка «Перейти в бота»

Хелпер telegramApi() описан в главе Первый запрос: getMe.

<?php
function answerInlineQuery(string $inlineQueryId, array $results, array $options = []): array {
$payload = array_merge([
'inline_query_id' => $inlineQueryId,
'results' => json_encode($results),
], $options);

return telegramApi('answerInlineQuery', $payload);
}

Типы результатов (InlineQueryResult)

Каждый результат — объект с обязательным type и id (уникальный в пределах ответа). Основные типы:

  • article — текстовое сообщение с заголовком, описанием, input_message_content (InputTextMessageContent)
  • photo — фото по URL (photo_url, thumb_url)
  • video — видео (video_url, mime_type, thumb_url)
  • document — файл (document_url, mime_type)
  • audio, voice, location, venue, contact, game, sticker

Для article и типов без файла используется input_message_content — объект с полями message_text, parse_mode, entities, link_preview_options.

Пример: поиск статей

Допустим, у нас есть локальный массив статей. Обрабатываем inline_query внутри processUpdate() (глава Long polling).

<?php
function handleInlineQuery(array $update): void {
$iq = $update['inline_query'];
$query = mb_strtolower(trim($iq['query'] ?? ''));
$offset = (int)($iq['offset'] ?? 0);
$limit = 20;

// Пример данных — в продакшене это БД/поисковый движок
$articles = [
['id' => '1', 'title' => 'PHP 8.3: новинки', 'text' => 'JIT, typed class constants, json_validate...'],
['id' => '2', 'title' => 'Telegram Bot API 7.0', 'text' => 'Stars, Mini Apps, reply parameters...'],
['id' => '3', 'title' => 'PSR-18 HTTP Client', 'text' => 'Стандарт интерфейсов для HTTP-клиентов.'],
];

$filtered = array_filter($articles, fn($a) => $query === '' ||
mb_stripos($a['title'], $query) !== false ||
mb_stripos($a['text'], $query) !== false);
$filtered = array_values($filtered);
$page = array_slice($filtered, $offset, $limit);

$results = array_map(function ($art) {
return [
'type' => 'article',
'id' => $art['id'],
'title' => $art['title'],
'description' => mb_substr($art['text'], 0, 100),
'input_message_content' => [
'message_text' => "<b>{$art['title']}</b>\n{$art['text']}",
'parse_mode' => 'HTML',
],
];
}, $page);

$nextOffset = (count($filtered) > $offset + $limit) ? (string)($offset + $limit) : '';

answerInlineQuery($iq['id'], $results, [
'cache_time' => 60,
'next_offset' => $nextOffset,
'is_personal' => true,
]);
}

Обработка выбранного результата

Когда пользователь выбирает результат, приходит апдейт chosen_inline_result с полями:

  • result_id — id выбранного результата
  • from — пользователь
  • query — исходный запрос
  • inline_message_id — id отправленного сообщения (для редактирования через editMessageText с inline_message_id)

Это полезно для аналитики и для последующего редактирования вставленного сообщения.

<?php
function handleChosenInlineResult(array $update): void {
$cir = $update['chosen_inline_result'];
// Логируем выбор, можно обновить счётчик популярности
error_log("Chosen: {$cir['result_id']} by user {$cir['from']['id']} query: {$cir['query']}");

// Если нужно отредактировать вставленное сообщение позже:
// $inlineMessageId = $cir['inline_message_id'];
// telegramApi('editMessageText', [
// 'inline_message_id' => $inlineMessageId,
// 'text' => 'Обновлённый текст',
// 'parse_mode' => 'HTML',
// ]);
}

Интеграция в процесс обновлений

Добавьте ветку в processUpdate():

<?php
function processUpdate(array $update): void {
if (isset($update['message'])) {
handleMessage($update['message']);
} elseif (isset($update['callback_query'])) {
handleCallbackQuery($update['callback_query']);
} elseif (isset($update['inline_query'])) {
handleInlineQuery($update);
} elseif (isset($update['chosen_inline_result'])) {
handleChosenInlineResult($update);
}
}

Важные нюансы

  • Результаты должны возвращаться быстро — таймаут ответа ~1-2 сек. Для тяжёлых запросов используйте next_offset и фоновые воркеры.
  • cache_time > 0 позволяет клиенту не дергать бота при повторном вводе того же запроса.
  • is_personal: true — результаты не кэшируются глобально, только для конкретного пользователя.
  • Максимум 50 результатов за один ответ. Для больших выборок — пагинация через next_offset.
  • Inline mode работает в группах, каналах (если бот админ) и личных чатах.

Дальше: Безопасность