Skip to content

Bot API 10.0 раскатился по всей экосистеме, а сцены стали композерами

8 – 31 мая 2026

Две главные истории этого цикла. Первая — Telegram Bot API 10.0 раскатился по всему стеку: @gramio/types v10, @gramio/contexts v0.7, @gramio/files v0.5, @gramio/format v0.8 и gramio v0.10 — с живыми фото, гостевыми сообщениями, медиа в опросах, правами на реакции и настройками доступа бота. Вторая — @gramio/scenes v0.7 наконец приносит давно обещанный редизайн сцена-как-композер: теперь каждая Scene — это полноценный EventComposer, каждый шаг — свой собственный саб-композер с .enter/.exit/.fallback, сцены собираются в переиспользуемые модули шагов, и появился новый лайфсайкл-хук onExit. Плюс @gramio/onboarding v0.2 прокидывает ctx.onboarding через bot.extend() вообще без церемоний.

Bot API 10.0 — живые фото, гостевые сообщения, медиа в опросах

@gramio/types v10.0.0 перегенерировал весь набор типов под Telegram Bot API 10.0, а @gramio/contexts v0.7.0 подсветил его геттерами и миксинами из коробки.

Новое вложение: живые фото

Новый LivePhotoAttachment плюс Message.livePhoto, ExternalReplyInfo.livePhoto и миксин sendLivePhoto:

ts
bot.on("message", (ctx) => {
    if (ctx.livePhoto) return ctx.send("Классное живое фото!");
});

await bot.api.sendLivePhoto({ chat_id, live_photo: "/path/to/live.mov" });

Гостевые сообщения

В Bot API 10.0 появились гостевые сообщения — новый апдейт guest_message, где пользователь взаимодействует с ботом без обычного чата. GramIO маппит его в MessageContext, отдаёт Message.guestQueryId / guestBotCallerUser / guestBotCallerChat, добавляет MessageContext.answerGuestQuery() и User.supportsGuestQueries(). Шорткат уровня фреймворка bot.guestQuery(...) приехал в gramio v0.10 (ниже).

В опросах появилось медиа

Опросы и их варианты теперь могут нести медиа. У Poll появились media, explanationMedia, membersOnly и countryCodes; у PollOptionmedia. sendPoll принимает медиа по каждому варианту, а correctOptionId по требованию API стал correctOptionIds (массивом).

Реакции, которые можно удалять, и права на реакции

  • У NodeMixin появились deleteReaction и deleteAllReactions.
  • ChatPermissions.canReactToMessages и ChatMember.canReactToMessages() отдают новый бит прав на реакции.
  • В ChatMemberControlMixin добавились getUserPersonalChatMessages, getManagedBotAccessSettings и setManagedBotAccessSettings; за ними стоят новые структуры BotAccessSettings / SentGuestMessage.

@gramio/files v0.5.0 и @gramio/format v0.8.0 перегенерированы

@gramio/files перевёл генератор на @gramio/schema-parser (тянет живую схему вместо вшитого JSON) и перегенерировал MEDIA_METHODS — подхватив sendLivePhoto, sendPoll, setMyProfilePhoto плюс дополнительные поля cover/photo/live_photo у sendVideo, sendMediaGroup, sendPaidMedia и editMessageMedia. @gramio/format перегенерировал мутатор под sendLivePhoto, answerGuestQuery и новые форматируемые поля explanation_media / media по вариантам. Оба подняли peer-зависимость @gramio/types до ^10.0.0.

gramio v0.10.0 — гостевые запросы, инлайн-результаты по CallbackData, фикс апдейтов

Поддержка Bot API 10.0

gramio v0.10.0 подключил линейку зависимостей Bot API 10.0 (@gramio/types ^10, @gramio/contexts ^0.7, @gramio/files ^0.5, @gramio/format ^0.8, @gramio/test ^0.7) и добавил guest_message в фильтр allowed_updates.

bot.guestQuery() — обрабатываем гостевые сообщения

Новый шорткат по образу inlineQuery: принимает триггер string / RegExp / предикат (или ничего — чтобы ловить любое гостевое сообщение), захватывает args и поддерживает макро-опции. Отвечаем через ctx.answerGuestQuery(result), где result — один InlineQueryResult:

ts
import { InlineQueryResult, InputMessageContent } from "gramio";

bot.guestQuery(async (ctx) => {
    // пользователь дошёл до бота через гостевое сообщение
    await ctx.answerGuestQuery(
        InlineQueryResult.article("1", "Привет!", InputMessageContent.text("Привет, гость!")),
    );
});

// либо фильтруем по тексту гостевого запроса
bot.guestQuery(/^help/i, (ctx) =>
    ctx.answerGuestQuery(
        InlineQueryResult.article("help", "Помощь", InputMessageContent.text("Чем помочь?")),
    ),
);

Он намеренно отделён от command/hears/startParameter — у гостевых сообщений другая семантика ответа (answerGuestQuery, а не ctx.send/reply). Смотрите новую страницу триггера guestQuery.

chosenInlineResult принимает схему CallbackData

bot.chosenInlineResult(schema, handler) теперь фильтрует по result_id и распаковывает в типизированный ctx.queryData — ровно как callbackQuery(schema, …):

ts
import { CallbackData } from "gramio";

const card = new CallbackData("card").number("id");

bot.inlineQuery(/cards/, (ctx) =>
    ctx.answer([
        InlineQueryResult.article(card.pack({ id: 42 }), "Карточка #42", /* … */),
    ]),
);

bot.chosenInlineResult(card, (ctx) => {
    ctx.queryData.id; // ✅ типизирован как number
});

Триггеры string / RegExp / предикат по-прежнему матчатся по query.

Перегрузка bot.inlineQuery(handler) без триггера

bot.inlineQuery(handler) (без триггера) теперь матчит любой инлайн-запрос — удобно для паттерна с редиректом на авторизацию, когда вы отвечаете пустым набором результатов плюс кнопкой логина.

Авторам плагинов: реэкспортнуты subclass-overlay хелперы

WithDerives, WithEventDerive, WithDecorate, WithExtend и DeriveHandler теперь реэкспортятся прямо из gramio, так что авторам плагинов, которые строят классы поверх Composer (как Scene), не надо лезть в @gramio/composer.

Фикс: апдейты не теряются при остановке посреди батча

Когда bot.stop() переключал isStarted в false, пока батч getUpdates был в полёте, offset продвигался локально (подтверждая батч на стороне Telegram), а сам батч дропался — апдейты молча терялись. v0.10 бросает такой батч без продвижения offset, и Telegram передоставит его при следующем старте.

Фикс: типизированные боты присваиваются в webhookHandler

Боты с derive/плагинами/макросами нельзя было передать в webhookHandler без as any, потому что их дженерики сидят в контравариантных позициях. Параметр расширили до AnyBot — каст больше не нужен.

@gramio/scenes v0.7 — сцены стали композерами

Самый крупный релиз сцен за всё время. Теперь Scene наследует EventComposer, так что весь DSL уровня бота — .use/.on/.derive/.decorate/.guard/.command/.callbackQuery/.hears/… — доступен прямо на сцене. А поверх этого фундамента — три больших DX-улучшения.

Builder-шаги — каждый шаг это свой саб-композер

Новый рекомендуемый способ описать шаг: передаём builder-колбэк, который получает посшаговый композер с лайфсайкл-хуками .enter / .exit / .fallback / .message плюс полный набор событий (.on / .command / .callbackQuery / .hears):

ts
import { Scene } from "@gramio/scenes";

const checkout = new Scene("checkout")
    .step("ask-name", (c) =>
        c
            .message("Как тебя зовут?") // сахар над .enter(ctx => ctx.send(...))
            .on("message", (ctx) => ctx.scene.update({ name: ctx.text })),
    )
    .step("confirm", (c) =>
        c
            .enter((ctx) => ctx.send(`${ctx.scene.state.name}, подтверждаем? (да/нет)`))
            .hears("да", (ctx) => ctx.scene.exit())
            .fallback((ctx) => ctx.send("Ответь да или нет")),
    );

Состояние выводится из ctx.scene.update() автоматически

Builder-шаги прокидывают форму, которую ты передал в ctx.scene.update({...}), в ctx.scene.state для всех последующих шагов — никакого .state<T>() объявлять не нужно:

ts
new Scene("signup")
    .step("ask", (c) =>
        c.on("message", (ctx) => ctx.scene.update({ name: ctx.text! })),
    )
    .step("greet", (c) =>
        c.enter((ctx) => {
            ctx.scene.state.name; // ✅ выведен как string
        }),
    );

Переиспользуемые модули шагов — scene.extend(otherScene)

Описываем безымянную Scene как переиспользуемый блок шагов и .extend()-им её в любую именованную сцену. Коллизии именованных шагов бросают ошибку; числовые шаги перенумеровываются автоматически:

ts
// Переиспользуемый модуль подтверждения — войти в него напрямую нельзя
const confirm = new Scene().step("confirm", (c) =>
    c
        .enter((ctx) => ctx.send("Уверены?"))
        .callbackQuery("yes", (ctx) => ctx.scene.step.next())
        .callbackQuery("no", (ctx) => ctx.scene.exit()),
);

const order = new Scene("order")
    .step("review", (c) => c.enter((ctx) => ctx.send("Проверь заказ")))
    .extend(confirm) // ← подтягивает шаг "confirm"
    .step("done", (c) => c.enter((ctx) => ctx.send("Заказ оформлен!")));

Лайфсайкл-хук onExit + derive виден в onEnter

Симметрично onEnter, новый onExit срабатывает при выходе из сцены (через exit(), exitSub() или reenter()) до того, как хранилище сносится — идеально для очистки или сообщения «спасибо, что прошли». А ещё результаты .derive() уровня сцены теперь видны внутри onEnter, так что можно загрузить данные и среагировать на них при входе в одной цепочке:

ts
const checkout = new Scene("checkout")
    .derive(async (ctx) => ({ user: await db.users.find(ctx.from!.id) }))
    .onEnter((ctx) => analytics.track("checkout_start", { user: ctx.user }))
    .onExit((ctx) => ctx.send("Спасибо, что заглянули!"))
    .step("review", (c) => c.message("Всё ок?").on("message", confirm));

Классическая форма шага через фильтр события — .step("message", handler) и .step(["message", "callback_query"], handler)полностью поддерживается и работает рядом с builder-шагами в одной сцене. В новом коде тянись к builder-шагам ради аккуратного посшагового лайфсайкла и автовывода состояния. В гайде по сценам обе формы лежат бок о бок.

v0.7.1 — derive, читаемые в onEnter, снова выполняются ровно один раз

Быстрый фолоу-ап патч: 0.7.0 мог выполнять .derive() уровня сцены дважды на входном апдейте, когда его результат читался внутри onEnter (один раз, чтобы onEnter его увидел, второй — в цепочке диспетчеризации). @gramio/scenes 0.7.1 возвращает выполнение ровно один раз за апдейт — важно, если у твоего derive есть сайд-эффекты (счётчики, спаны, запись в БД). Обновляйся сразу до 0.7.1.

@gramio/onboarding v0.2.0 — типизированный build()

createOnboarding({ id: "welcome" }).…​.build() раньше возвращал плоский Plugin, стирая форму derive — поэтому ctx.onboarding.welcome требовал module augmentation или каст на каждом вызове. v0.2 прокидывает Id флоу через весь билдер, так что теперь bot.extend(...) расширяет ctx.onboarding.welcome автоматически:

ts
import { Bot } from "gramio";
import { createOnboarding } from "@gramio/onboarding";

const bot = new Bot(process.env.BOT_TOKEN!).extend(
    createOnboarding({ id: "welcome" })
        .step("hi", { text: "Привет!" })
        .step("done", { text: "Готово!" })
        .build(),
);

bot.command("start", (ctx) => {
    ctx.onboarding.welcome.start(); // ✅ типизирован, без augmentation
    return ctx.send("Поехали!");
});

Рантайм не изменился — только уровень типов — но те, кто полагался на старый возврат плоского Plugin, подхватят более строгий вывод.

Документация и скиллы

  • Новая страница триггера guestQuery с описанием флоу гостевых сообщений из Bot API 10.
  • Доки по сценам пересобраны так, чтобы вести с builder-API шагов, оставив форму через фильтр события как равноценную альтернативу.
  • Новый референс deep-links в AI-скиллах — единый источник правды по каждому семейству ссылок t.me/<bot>?… (?start=, ?startapp=, ?startgroup=, кодирование payload, токены admin=), чтобы агенты роутили каждую ссылку правильно.
  • Страницы референса методов/типов Telegram обновлены до Bot API 10.0, плюс проход по twoslash + Vite по ~200 сниппетам.