inline mode

let users type @your_bot query in any chat and pick a result generated by your bot.

answer inline queries

inline query handlers must answer with a list of results. keep the response fast, cache where possible, and use is_personal when results depend on the current user. ctx.answer() takes the results array positionally, plus an options object for cache_time/is_personal/next_offset.

inline-query.ts
import { createBot } from "yaebal";

const search = async (query: string) => [{ id: "1", title: "result", text: "text" }];
const bot = createBot(process.env.BOT_TOKEN!);

bot.on("inline_query", async (ctx) => {
  const results = await search(ctx.query);

  await ctx.answer(
    results.map((item) => ({
      type: "article" as const,
      id: item.id,
      title: item.title,
      input_message_content: { message_text: item.text },
    })),
    { cache_time: 10, is_personal: true },
  );
});

build results without hand-rolled objects

@yaebal/inline-results ships one typed factory per InlineQueryResult/InputMessageContent variant — required fields are positional, everything else is a trailing options object, so a result reads as data instead of an object literal that can silently drift from the schema.

inline-query-builder.ts
import { createBot } from "yaebal";
import { InlineQueryResult, InputMessageContent } from "@yaebal/inline-results";

const search = async (query: string) => [{ id: "1", title: "result", url: "https://example.com" }];
const bot = createBot(process.env.BOT_TOKEN!);

bot.on("inline_query", async (ctx) => {
  const items = await search(ctx.query);

  await ctx.answer(
    items.map((item) =>
      InlineQueryResult.article(item.id, item.title, InputMessageContent.text(item.url)),
    ),
    { cache_time: 10 },
  );
});

track chosen results

if enabled in botfather, telegram sends chosen_inline_result after a user picks one of your results. use it for analytics, not for critical business logic.

chosen-result.ts
import { createBot } from "yaebal";

declare const analytics: { track(event: string, props: Record<string, unknown>): Promise<void> };
const bot = createBot(process.env.BOT_TOKEN!);

bot.on("chosen_inline_result", async (ctx) => {
  await analytics.track("inline_chosen", {
    resultId: ctx.result_id,
    userId: ctx.from.id,
    query: ctx.query,
  });
});

switch inline buttons

inline keyboard buttons can open the inline picker with a prefilled query. this is useful for sharing products, documents, search results, or mini app content.

keyboard.ts
import { InlineKeyboard } from "yaebal";

new InlineKeyboard()
  .switchInline("share", "product:42")
  .row()
  .switchInlineCurrentChat("search here", "cats")
  .build();

rules of thumb

  • return stable result ids so analytics and caches stay meaningful.
  • keep result payloads small; the user is waiting inside telegram's inline picker.
  • use cache_time aggressively for public search results.
  • set is_personal for private/user-specific results.
  • add inline_query and chosen_inline_result to allowedUpdates when polling.