Skip to content

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/types10.3.1Опубликован
2@gramio/contexts0.11.0Опубликован
2@gramio/files0.8.0Опубликован
2@gramio/format0.11.0Опубликован
2@gramio/keyboards1.5.0Опубликован
2@gramio/callback-data0.2.0Опубликован
2wrappergram2.0.0Опубликован
3gramio0.14.0Опубликован
4@gramio/test0.8.0Опубликован
4@gramio/jsx0.1.0Опубликован
4@gramio/views0.2.1Опубликован
5create-gramio2.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:

ts
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.

У отдельных методов редактирования и удаления адресные поля остаются наверху:

ts
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 один-в-один и не добавляет устаревшие алиасы.

ts
// 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, нужно добавить новое обязательное право:

ts
const fixture = {
  ...existingAdministrator,
  can_send_welcome_messages: false,
};

Stop-поля черновиков защищены от регрессий генератора

Оба draft-метода предоставляют одинаковые настройки:

ts
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 методы отправки.

ts
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, состояние и удобные предикаты:

ts
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 сохраняет исходный тред.
ts
await ctx.download().toFile("./photo.jpg");
const metadata = await ctx.download().info();

@gramio/format 0.11 — Rich Messages удобно собирать руками

gramio/rich и @gramio/format/rich дают экранируемые композируемые хелперы — больше не нужно вручную собирать каждый InputRichBlock*.

ts
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.

ts
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>.

tsx
/** @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:

ts
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, не меняя то, что видит пользователь:

ts
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-хэндлеру:

ts
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:

ts
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.

ts
// 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);
ts
// 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 — при отправке и редактировании поддерживаемых медиа:

ts
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.

bash
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.