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:
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; у PollOption — media. 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:
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, …):
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):
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>() объявлять не нужно:
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()-им её в любую именованную сцену. Коллизии именованных шагов бросают ошибку; числовые шаги перенумеровываются автоматически:
// Переиспользуемый модуль подтверждения — войти в него напрямую нельзя
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, так что можно загрузить данные и среагировать на них при входе в одной цепочке:
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 автоматически:
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 сниппетам.