Rich Messages приехали, эфемерные ответы стали приватными, Bot API 10.3 раскатился по всему стеку
31 мая – 25 августа 2026
За этот цикл через всю экосистему GramIO прошли сразу три релиза Telegram. Bot API 10.1 принёс Rich Messages и запросы на вступление, 10.2 добавил структурированный rich-input, управление эфемерными сообщениями, Communities и апдейты подписок, а 10.3 завершил картину приватными параметрами доставки, останавливаемыми черновиками, отключёнными кнопками, расширяемыми цитатами, документами и компактными таблицами.
Теперь весь этот API поддерживается не только типами: он проходит через контексты, format-хелперы, рекурсивные загрузки, конструкторы клавиатур, JSX, тестовый рантайм и генератор новых проектов. Это согласованная релизная линейка, а не очередная регенерация деклараций.
Статус релиза
Вся релизная линейка опубликована. @gramio/types 10.3.1 содержит одинаковые декларации в npm и JSR, а каждый зависимый пакет из таблицы ниже доступен в npm с provenance.
Релизная линейка
| Порядок | Пакет | Целевая версия | Статус |
|---|---|---|---|
| 1 | @gramio/types | 10.3.1 | Опубликован |
| 2 | @gramio/contexts | 0.11.0 | Опубликован |
| 2 | @gramio/files | 0.8.0 | Опубликован |
| 2 | @gramio/format | 0.11.0 | Опубликован |
| 2 | @gramio/keyboards | 1.5.0 | Опубликован |
| 2 | @gramio/callback-data | 0.2.0 | Опубликован |
| 2 | wrappergram | 2.0.0 | Опубликован |
| 3 | gramio | 0.14.0 | Опубликован |
| 4 | @gramio/test | 0.8.0 | Опубликован |
| 4 | @gramio/jsx | 0.1.0 | Опубликован |
| 4 | @gramio/views | 0.2.1 | Опубликован |
| 5 | create-gramio | 2.3.0 | Опубликован |
Линейка публиковалась именно в этом порядке, чтобы каждый пакет использовал уже проверенный слой зависимостей. Неизменившиеся плагины остаются на прежних версиях, а не бампятся ради симметрии; Views получил patch-релиз только потому, что Bot API 10.3 изменил media union на уровне типов.
@gramio/types 10.1–10.3.1 — три релиза Telegram, один строгий контракт
Bot API 10.1: Rich Messages и запросы на вступление
Bot API 10.1 добавил read-side для Rich Messages: RichText*, RichBlock*, Message.rich_message, InputRichMessage, sendRichMessage, sendRichMessageDraft и rich-редактирование. В том же релизе появились join-request queries и ссылочные медиа для опросов.
Теперь бот может сразу решить судьбу заявки или сначала открыть пользователю Mini App:
await bot.api.sendChatJoinRequestWebApp({
chat_join_request_query_id: queryId,
web_app_url: "https://example.com/join-review",
});
await bot.api.answerChatJoinRequestQuery({
chat_join_request_query_id: queryId,
result: "approve",
});На стороне контекстов возможности видны через User.supportsJoinRequestQueries, ChatFullInfo.guardBot и ChatJoinRequest.queryId.
Bot API 10.2: структурированный rich-input, Communities, подписки и управление ephemeral
Rich Messages получили полную write-side модель: абзацы, списки, таблицы, коллажи, слайд-шоу, details, thinking, математику, медиа, документы и voice-note блоки. Вместе с ними Bot API 10.2 добавил:
editEphemeralMessageText,editEphemeralMessageMedia,editEphemeralMessageCaption,editEphemeralMessageReplyMarkupиdeleteEphemeralMessage;Message.receiver_user,Message.ephemeral_message_idи reply-target для эфемерного сообщения;Community,community_chat_addedиcommunity_chat_removed;- корневой апдейт
subscriptionсо статусамиactive,canceledиfailed.
У отдельных методов редактирования и удаления адресные поля остаются наверху:
await bot.api.editEphemeralMessageText({
chat_id: chatId,
receiver_user_id: userId,
ephemeral_message_id: ephemeralId,
text: "Updated private result",
});
await bot.api.deleteEphemeralMessage({
chat_id: chatId,
receiver_user_id: userId,
ephemeral_message_id: ephemeralId,
});BREAKING: Bot API 10.3 вкладывает параметры эфемерной отправки
Методы отправки больше не принимают receiver_user_id и callback_query_id на верхнем уровне. GramIO намеренно повторяет Telegram один-в-один и не добавляет устаревшие алиасы.
// Before
await bot.api.sendMessage({
chat_id: 42,
text: "Private result",
receiver_user_id: userId,
callback_query_id: callbackQueryId,
});
// Bot API 10.3
await bot.api.sendMessage({
chat_id: 42,
text: "Private result",
ephemeral_message_parameters: {
receiver_user_id: userId,
callback_query_id: callbackQueryId,
replace_callback_query_message: true,
},
});Это правило касается только отправки. У показанных выше edit/delete методов receiver и ephemeral ID остаются наверху: они адресуют существующее сообщение, а не настраивают новую доставку.
В фикстуры, которые вручную создают ChatAdministratorRights или ChatMemberAdministrator, нужно добавить новое обязательное право:
const fixture = {
...existingAdministrator,
can_send_welcome_messages: false,
};Stop-поля черновиков защищены от регрессий генератора
Оба draft-метода предоставляют одинаковые настройки:
await bot.api.sendRichMessageDraft({
chat_id: 42,
draft_id: 1001,
rich_message: { markdown: "## Generating…" },
can_stop: true,
keep_on_stop: true,
});SendMessageDraftParams и SendRichMessageDraftParams теперь проверяются generation assertions. Если Telegram или парсер снова потеряет can_stop либо keep_on_stop, генерация завершится ошибкой, а не молча выпустит неполные декларации.
Полный жизненный цикл отправки, замены, редактирования, удаления и ответа разобран в гайде по эфемерным сообщениям.
@gramio/contexts 0.11 — каждому новому апдейту свой контекст
Апдейт остановки генерации не требует opt-in
MessageGenerationStoppedContext обрабатывает stopped_message_generation и предоставляет draftId, threadId, chat, chatId, chatType, клонирование и thread-aware методы отправки.
bot.on("stopped_message_generation", async (ctx) => {
console.log(ctx.draftId, ctx.threadId, ctx.chatId, ctx.chatType);
await ctx.send("Generation stopped.");
});Communities и подписки становятся полноценными событиями
CommunityChatAddedContext, CommunityChatRemovedContext и новый CommunityChatJoinedContext покрывают весь жизненный цикл Community. В контексте подписки доступны пользователь, invoice payload, состояние и удобные предикаты:
bot.on("community_chat_joined", (ctx) =>
ctx.send(`Joined community ${ctx.community.id}`),
);
bot.on("subscription", (ctx) => {
if (ctx.isFailed) {
console.warn(`Subscription failed for ${ctx.user.id}`);
}
});Обёртки администраторов и участников получили canSendWelcomeMessages(). В UniqueGiftInfo появились text, обёрнутые entities и isPrivate. Структуры клавиатур отдают forceReply и состояние disabled-кнопки.
Rich Messages снова превращаются в полезный plain text
Инспекция контекста и fallback text extraction теперь учитывают rich-кнопки, ряды кнопок, раскрываемые цитаты, авторство и подписи документов. Боты, которые логируют, ищут, суммаризируют или тестируют Rich Messages, больше не теряют эти видимые строки.
gramio 0.14 — строгий raw API, полная маршрутизация и удобные файлы
Core двигает всю линейку зависимостей вместе и сохраняет bot.api.* один-в-один с Telegram. stopped_message_generation входит в AllowedUpdatesFilter.default, AllowedUpdatesFilter.all и фильтры, которые выводятся из зарегистрированных хэндлеров. В отличие от chat_member и реакций, вручную включать этот апдейт не нужно.
Что ещё приехало после прошлого changelog
В цикле 10.1/10.2 появились и полезные framework-изменения вне самой поверхности Bot API:
ctx.download()иbot.downloadFile()возвращают ленивый Response-like handle с.bytes(),.text(),.json(),.blob(),.stream(),.toFile(),.link()и.info();- callback-query контексты передают topic/thread информацию, поэтому send mixin сохраняет исходный тред.
await ctx.download().toFile("./photo.jpg");
const metadata = await ctx.download().info();@gramio/format 0.11 — Rich Messages удобно собирать руками
gramio/rich и @gramio/format/rich дают экранируемые композируемые хелперы — больше не нужно вручную собирать каждый InputRichBlock*.
import { bold, format } from "gramio";
import {
button,
buttonRow,
document,
heading,
paragraph,
quote,
rich,
table,
} from "gramio/rich";
await ctx.send(
rich([
heading(1, "Release report"),
paragraph(format`Status: ${bold`ready`}`),
quote("Expandable details", {
expandable: true,
credit: "Build system",
}),
document({
url: "https://example.com/report.pdf",
caption: "Full report",
}),
table(
[
["Package", "Version"],
["gramio", "0.14.0"],
],
{ compact: true, align: ["left", "right"] },
),
buttonRow(
[
button("Open", {
type: "url",
url: "https://gramio.dev",
}),
button("Soon", { type: "disabled" }),
],
{ align: "right" },
),
]),
);Сгенерированный formatting mutator также покрывает rich-контент в эфемерных edits и новые пути документов/подписей.
@gramio/files 0.8 — загрузки рекурсивно следуют за rich-контентом
File extraction теперь проходит ссылки, union-типы, массивы и рекурсивные rich-блоки. Он понимает загрузки внутри sendRichMessage.rich_message.blocks, .media, вложенных метаданных и InputRichBlockDocument.
import { MediaUpload } from "gramio";
await bot.api.sendRichMessage({
chat_id: chatId,
rich_message: {
html: '<tg-document src="tg://document?id=report"></tg-document>',
media: [
{
id: "report",
media: {
type: "document",
media: await MediaUpload.path("./report.pdf"),
},
},
],
},
});Старая форма Extractor остаётся доступной для downstream-интеграций; в сгенерированных метаданных появляются wildcard path descriptors для глубокого контента. sendRichMessageDraft намеренно исключён: Telegram запрещает прямые загрузки в черновики, поэтому там нужен готовый file_id.
@gramio/jsx 0.1 — Rich layouts через JSX
Rich JSX runtime добавляет <button>, <button-row>, <document>, раскрываемый <blockquote> с credit и компактный <table>.
/** @jsxImportSource @gramio/jsx/rich */
const report = (
<rich>
<h1>Release report</h1>
<blockquote expandable credit="Build system">
Expandable details
</blockquote>
<document url="https://example.com/report.pdf" caption="Full report" />
<table compact align={["left", "right"]}>
<tr>
<th>Package</th>
<th>Version</th>
</tr>
<tr>
<td>gramio</td>
<td>0.14.0</td>
</tr>
</table>
<button-row align="right">
<button type="url" url="https://gramio.dev">
Open
</button>
<button type="disabled">Soon</button>
</button-row>
</rich>
);
await ctx.send(report);Обычный keyboard JSX runtime тоже поддерживает disabled-кнопки и forceReply.
@gramio/keyboards 1.5 — отключённые действия и Force Reply
Конструкторы клавиатур теперь умеют создавать отключённые inline-кнопки Telegram и выставлять force_reply:
import { InlineKeyboard, Keyboard } from "gramio";
const inline = new InlineKeyboard()
.text("Run", "run")
.disabled("Unavailable", { style: "primary" })
.forceReply();
const reply = new Keyboard().text("Share status").forceReply();.forceReply(false) явно сериализует force_reply: false — удобно, когда общий builder настраивается по условию.
@gramio/callback-data 0.2 — скрытые payload для reply-клавиатур
У inline-клавиатур есть callback_data, а reply-кнопки отправляют видимый label обратно как обычный текст сообщения. Новый zero-width codec добавляет к label упакованный payload, не меняя то, что видит пользователь:
import { embed, extract } from "@gramio/callback-data";
import { CallbackData, Keyboard } from "gramio";
const navigation = new CallbackData("reply-navigation").enum("to", ["settings"]);
const keyboard = new Keyboard().text(
embed("⚙️ Settings", navigation.pack({ to: "settings" })),
);
bot.on("message", (ctx) => {
const embedded = extract(ctx.text ?? "");
if (!embedded) return;
const action = navigation.safeUnpack(embedded.data);
if (!action.success) return;
return ctx.send(`Opening ${action.data.to}`);
});encode() и decode() дают доступ к низкоуровневому невидимому кодеку, а embed() и extract() работают с полными видимыми label. Считайте встроенный payload транспортными данными и проверяйте его через CallbackData.safeUnpack() перед использованием.
@gramio/test 0.8 — останавливаем draft и проверяем приватную отправку
User actor теперь умеет останавливать незавершённый черновик. Test environment доставляет тот же типизированный апдейт, который придёт production-хэндлеру:
import { TelegramTestEnvironment } from "@gramio/test";
const env = new TelegramTestEnvironment(bot);
const user = env.createUser({ first_name: "Ada" });
await user.stopMessageGeneration(1001, {
messageThreadId: 7,
});Моки эфемерной отправки читают вложенные параметры и возвращают реалистичные receiver_user и ephemeral_message_id:
const sent = await bot.api.sendMessage({
chat_id: user.payload.id,
text: "Private result",
ephemeral_message_parameters: {
receiver_user_id: user.payload.id,
},
});
expect(sent.receiver_user?.id).toBe(user.payload.id);
expect(sent.ephemeral_message_id).toBeNumber();Compile-фикстуры отдельно проверяют, что устаревшие поля верхнего уровня не компилируются, — случайно вернуть compatibility aliases уже не получится.
wrappergram 2.0 — middleware-core и правильная raw-линейка 10.3
Wrappergram 2.0 сохраняет класс Telegram, но меняет устройство вызовов, ошибок и опциональных возможностей. Старая версия возвращала raw-envelope { ok, result } и тащила обработку файлов внутри. Вторая версия возвращает результат API напрямую, по умолчанию бросает TelegramError, а файлы и форматирование подключает отдельными middleware.
// Before: raw response envelope, file handling bundled in
const response = await telegram.api.sendMessage({
chat_id: chatId,
text: "Hello",
});
if (!response.ok) console.error(response.description);
else console.log(response.result.message_id);// Wrappergram 2.0
import { Telegram, TelegramError } from "wrappergram";
import { filesMiddleware } from "@gramio/files/middleware";
import { formatMiddleware } from "@gramio/format/middleware";
const telegram = new Telegram(token, {
middlewares: [formatMiddleware, filesMiddleware],
});
const result = await telegram.api.sendMessage({
chat_id: chatId,
text: "Hello",
suppress: true,
});
if (result instanceof TelegramError) {
console.error(result.method, result.code, result.payload);
} else {
console.log(result.message_id);
}requestOptions переименован в глобальный fetchOptions, а вторым аргументом каждого API-метода можно передать настройки конкретного fetch-запроса. Пользователей gramio эта миграция не касается — только прямых потребителей Wrappergram.
@gramio/views 0.2.1 — совместимость media с Bot API 10.3
Bot API 10.3 добавил InputMediaLivePhoto с отдельными photo- и video-input. Views намеренно описывает один загружаемый файл на каждый элемент .media(), поэтому версия 0.2.1 исключает live photo на границе типов, а не принимает объект, который не сможет правильно сериализовать.
Patch также сохраняет media-specific поля — например thumbnail и streaming flags — при отправке и редактировании поддерживаемых медиа:
return this.response.media({
type: "video",
media: videoFileId,
thumbnail: thumbnailFileId,
supports_streaming: true,
});Для live photo используйте raw-метод sendLivePhoto. Обычные photo, video, animation, audio, document, sticker, voice и video-note views продолжают поддерживаться.
create-gramio 2.3 — новые проекты сразу на новой линейке
Новые проекты используют gramio ^0.14.0 и @gramio/test ^0.8.0. Версии зафиксированы generator-тестами, поэтому шаблон не сможет незаметно откатиться на предыдущую линейку Bot API.
npm create gramio@latest my-bot
cd my-bot
bun install
bunx tsc --noEmitСовместимость, документация и rollout
- TDLight компилируется с локальным набором деклараций Types 10.3.1.
- Scenes, Session, i18n, Auto Answer Callback Query, Onboarding, Rate Limit, PostHog и Views прошли compatibility runs на source-стеке.
- Views 0.2.1 исключает новый двухфайловый
InputMediaLivePhotoиз своей однофайловой media-абстракции и сохраняет полные editable media objects. - Ecosystem CI проходит 17 downstream suites с Types из source и 24 suites против опубликованной линейки
latest. - Telegram API Reference перегенерирован под Bot API 10.3.
- Новые EN/RU гайды разбирают эфемерные сообщения и Rich Messages.
- В AI-скилле GramIO появился rich-message reference и запускаемый тестируемый пример; upgrade data покрывает каждый пакет релизной линейки.
Вся линейка доступна в npm, а декларации Types 10.3.1 совпадают между npm и JSR. Каждый слой, сгенерированный проект, неизменившийся потребитель и опубликованная линейка пакетов прошли свои release gates.