Legan Studio
Все статьи
~ 5 мин чтения

Inline-режим Telegram-бота: @bot query в любом чате

Inline-mode: как включить, как обработать inline_query, кеширование, превью и платежи через inline. Реальные сценарии: поиск, шаблоны, мемы.

  • Telegram
  • inline
  • Bot API
  • поиск

Inline-режим — это способ вызвать бота прямо в любом чате, набрав @bot query. Telegram показывает выпадающий список результатов, юзер выбирает один, и сообщение отправляется от его имени с пометкой «via @bot». Это самая «вирусная» фича Bot API: каждое отправленное сообщение — реклама вашего бота.

Разберём, как включить inline, как структурировать inline_query, как кешировать результаты, как обрабатывать выбор и как делать inline-платежи через Stars.

Когда inline-режим уместен

Inline хорошо ложится на сценарии, где юзеру нужно быстро вставить контент в чужой чат, не открывая бота:

  • Поиск гифок, стикеров, мемов (классика — @gif, @pic).
  • Поиск по базе знаний компании — менеджер в чате с клиентом набирает @helpbot тариф pro.
  • Ссылки и шаблоны@templates КП на сайт.
  • Калькуляторы@calcbot ипотека 5 млн 20 лет.
  • Микро-платежи@tipbot 100 stars.

Не подходит для длинных диалогов, заявок, FSM-сценариев — там нужен обычный чат с ботом.

Включение inline

В @BotFather/mybots → выбираете бота → Bot SettingsInline ModeTurn On. Дополнительно настраивается placeholder («Введите запрос…») и feedback collection (отправка статистики о выбранных результатах).

Без включения в BotFather бот не получит ни одного inline_query, даже если код готов.

Обработка inline_query

from aiogram import Router
from aiogram.types import (
    InlineQuery, InlineQueryResultArticle, InputTextMessageContent
)

router = Router()

@router.inline_query()
async def search(query: InlineQuery):
    text = query.query.strip().lower()
    if not text:
        results = await top_articles(limit=10)
    else:
        results = await search_articles(text, limit=20)

    answer = [
        InlineQueryResultArticle(
            id=str(a.id),
            title=a.title,
            description=a.summary[:80],
            thumbnail_url=a.thumb,
            input_message_content=InputTextMessageContent(
                message_text=f"<b>{a.title}</b>\n\n{a.url}",
                parse_mode="HTML",
            ),
        )
        for a in results
    ]
    await query.answer(
        results=answer,
        cache_time=60,
        is_personal=False,
        next_offset=str(len(results)) if len(results) == 20 else "",
    )

cache_time — Telegram кеширует результат на N секунд для одинакового query, чтобы не дёргать бота при каждом нажатии клавиши. is_personal=True — кеш только для конкретного юзера (нужно, если результаты зависят от профиля).

Пагинация inline

Telegram запрашивает следующую пачку через тот же inline_query, передавая offset, который вы вернули в next_offset:

@router.inline_query()
async def search(query: InlineQuery):
    offset = int(query.offset or 0)
    results = await search_articles(query.query, limit=20, offset=offset)
    await query.answer(
        results=build_results(results),
        next_offset=str(offset + 20) if len(results) == 20 else "",
        cache_time=10,
    )

Пустой next_offset — конец списка. Telegram перестанет запрашивать дальше.

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

Bot API поддерживает 20+ типов inline-результатов. Самые ходовые:

ТипКогда использовать
InlineQueryResultArticleпроизвольный текст с заголовком
InlineQueryResultPhotoфото из URL
InlineQueryResultGifгифка
InlineQueryResultVideoвидео
InlineQueryResultAudioаудио
InlineQueryResultVoiceголосовое
InlineQueryResultDocumentфайл (PDF, ZIP)
InlineQueryResultLocationгеолокация
InlineQueryResultContactконтакт
InlineQueryResultGameигра HTML5

Для медиа можно передавать как *_url (Telegram скачивает сам), так и *_file_id (если файл уже в Telegram — мгновенная отправка).

Кеширование на стороне бота

Bot API кеш cache_time отдаёт повторные запросы из своего кеша, не дёргая бота. Но для тяжёлых поисков по БД (Elasticsearch, PostgreSQL FTS) полезен и собственный кеш в Redis:

async def search_articles(text: str, limit=20, offset=0):
    key = f"inline:{text}:{offset}:{limit}"
    cached = await redis.get(key)
    if cached:
        return json.loads(cached)
    results = await db.full_text_search(text, limit=limit, offset=offset)
    await redis.set(key, json.dumps(results), ex=300)
    return results

Это критично при росте: 10000 inline-запросов в минуту без кеша легко положат БД.

chosen_inline_result: что выбрал юзер

Чтобы знать, какой результат юзер реально отправил, подпишитесь на chosen_inline_result. Telegram присылает этот апдейт только если бот включил Inline Feedback в BotFather.

from aiogram.types import ChosenInlineResult

@router.chosen_inline_result()
async def on_chosen(result: ChosenInlineResult):
    await analytics.track(
        "inline_chosen",
        user_id=result.from_user.id,
        result_id=result.result_id,
        query=result.query,
    )

Это золотая аналитика: вы видите, какие запросы конвертируются в реальные отправки, и можете оптимизировать выдачу.

Inline-платежи через Stars

С 2024 года через inline можно отправлять InlineQueryResultArticle с кнопкой «Купить за 50 Stars». При клике отправляется invoice-сообщение, юзер платит, бот ловит successful_payment как обычно.

result = InlineQueryResultArticle(
    id="tip_50",
    title="Чаевые 50 Stars",
    input_message_content=InputInvoiceMessageContent(
        title="Чаевые автору",
        description="Спасибо за крутой контент!",
        payload="tip_50",
        provider_token="",
        currency="XTR",
        prices=[LabeledPrice(label="Tip", amount=50)],
    ),
)

Так работают @tribute, @donate и другие чаевые-боты.

Топ-5 ошибок при inline

  1. Забывают включить inline в BotFather — inline_query не приходит.
  2. Не возвращают next_offset — Telegram прекращает пагинацию.
  3. Используют cache_time=300 для персонализированных результатов без is_personal=True — юзеры видят чужие.
  4. Не подписываются на chosen_inline_result — нет аналитики.
  5. Шлют тяжёлые URL-картинки без thumbnail_url — превью грузится 5+ секунд.

Итого

Inline-режим — это самый виральный канал для бота: каждое отправленное сообщение работает как реклама. Технически нужно включить inline в BotFather, обрабатывать inline_query с кешем 60+ секунд, корректно пагинировать через next_offset и подключить chosen_inline_result для аналитики. Хорошо встроенный inline даёт +30–50% к органическому росту бота за счёт виральности «via @bot».

Частые вопросы

Можно ли использовать inline без подписки на бота?

Да. Юзер вызывает @yourbot query в любом чате — даже если он никогда не запускал вашего бота. Это уникальное свойство inline: не требует /start. Имя бота юзер должен знать сам, поэтому в маркетинге обязательно показывайте полный handle с @.

Какой максимум результатов отдавать на один запрос?

Лимит Bot API — 50 результатов на ответ. На практике 10–20 хватает в 95% случаев: юзер прокручивает максимум 5–7 первых вариантов. Больше 50 → отдавайте через пагинацию next_offset.

Как сделать inline-результаты персонализированными?

Установите is_personal=True в answer_inline_query — Telegram не будет шарить кеш результатов между юзерами. Внутри обработчика читайте query.from_user.id и стройте выдачу по профилю: например, в @helpbot показывать только статьи, доступные конкретной компании.

Есть ли лимит на количество inline_query?

Bot API не штрафует за частые inline_query сами по себе, но действует общий лимит 30 сообщений в секунду (включая answer_inline_query). Если бот популярный (1000+ запросов в секунду), нужен внутренний кеш и батчинг — иначе возможны 429.

Можно ли в inline-результат добавить кнопки?

Да, через reply_markup: InlineKeyboardMarkup. Кнопки увидит получатель сообщения, и при нажатии прилетит обычный callback_query боту. Это отличный способ соединить вирусность inline с интерактивом callback.

Какой `cache_time` оптимален?

Для статических данных (топ-статьи, мемы, шаблоны) — 300–600 секунд. Для динамических (актуальные предложения, остатки) — 30–60 секунд. Для персонализированных с is_personal=True — обычно 60 секунд хватает: за это время юзер уже сделал выбор или закрыл чат.

Как продвигать бота через inline?

Главный приём — показывать в каждом сообщении бренд: «via @yourbot» отображается автоматически, но в самом тексте можно добавить призыв «Найди ещё в @yourbot». Также работают шаблоны с UTM в URL, чтобы считать, какие inline-сообщения приводят новых юзеров. Виральность 1.4–2.0 (один юзер приводит 1–2 новых) — нормальная цель.