Bot API 9.4, система Views, OpenTelemetry и streamMessage
8–15 февраля 2026
Невероятно насыщенная неделя. GramIO догоняет Bot API 9.4 — 8 новых контекстов. Появилась система шаблонов Views для переиспользуемых сообщений. Вышли плагины OpenTelemetry и Sentry. Добавлен хук onApiCall для инструментирования API-вызовов, streamMessage для стриминга черновиков с живой печатью, стилизация кнопок в клавиатурах, Node.js-поддержка SQLite и нативный Redis для Bun. Разбираем.
gramio v0.4.14 — Хук onApiCall и человечные стектрейсы
Новый хук: onApiCall
Седьмой хук в коллекции. onApiCall оборачивает весь жизненный цикл API-вызова — трейсинг, логирование, метрики, что угодно. Работает как middleware с next():
bot.onApiCall(async (context, next) => {
console.log(`→ ${context.method}`);
const start = Date.now();
const result = await next();
console.log(`← ${context.method} (${Date.now() - start}ms)`);
return result;
});Можно привязать к конкретным методам:
bot.onApiCall("sendMessage", async (context, next) => {
// срабатывает только для sendMessage
return next();
});Несколько хуков составляются как middleware — первый зарегистрированный оборачивает всё. Работает и в Bot, и в Plugin. Именно на этом построен новый плагин OpenTelemetry.
Стектрейсы ошибок теперь ведут на ваш код
TelegramError теперь запоминает место, откуда вы вызвали API. Когда bot.api.sendMessage(...) падает — стектрейс указывает на вашу строку кода, а не на внутренности фреймворка. Настройка не нужна — работает из коробки.
Зависимости
@gramio/contextsобновлён до^0.4.0@gramio/keyboardsобновлён до^1.3.0@gramio/typesобновлён до^9.4.0
@gramio/contexts v0.4.0 — Bot API 9.2 / 9.3 / 9.4
ea3049f aa3ff6d 3df81eb 524ba8b 13564e6
streamMessage — стриминг черновиков с живой печатью
Отправляйте текст чанками в чат с живым предпросмотром печати. Каждый чанк обновляет черновик через sendMessageDraft, а финальное сообщение отправляется через sendMessage при заполнении сегмента в 4096 символов. Идеально для стриминга ответов AI/LLM:
bot.command("stream", async (context) => {
const chunks = generateTextChunks(); // Iterable или AsyncIterable
const messages = await context.streamMessage(chunks);
});Принимает Iterable<MessageDraftPiece> или AsyncIterable<MessageDraftPiece>, где каждый элемент — строка или { text, entities?, draft_id? }. Поддерживает AbortSignal для отмены.
Bot API 9.2 — Предложенные посты и личные сообщения
8 новых контекстов и структур для жизненного цикла предложенных постов:
SuggestedPostApprovedContext,SuggestedPostApprovalFailedContext,SuggestedPostDeclinedContext,SuggestedPostPaidContext,SuggestedPostRefundedContext- Структура
DirectMessagesTopic Chat.isDirectMessages,ChatFullInfo.parentChat,ChatAdministratorRights.canManageDirectMessagesGift.publisherChat,UniqueGift.publisherChatMessage.suggestedPostInfo,Message.directMessagesTopic,Message.isPaidPost
Bot API 9.3 — Апгрейд подарков, новые структуры
GiftUpgradeSentContext— сервисное сообщение об апгрейде подарка- Метод
sendMessageDraft()в SendMixin - Новые структуры:
GiftBackground,UniqueGiftColors,UserRating - Расширен
Gift:isPremium,background,uniqueGiftVariantCount - Расширен
ChatFullInfo:rating,uniqueGiftColors,paidMessageStarCount
Bot API 9.4 — Смена владельца чата, качество видео, аудио профиля
ChatOwnerLeftContext,ChatOwnerChangedContext— отслеживание смены владельца чата- Структура
VideoQualityс полемcodec("h265"|"av01") - Структура
UserProfileAudios - В
VideoAttachmentдобавлен геттерqualities - В
UniqueGiftModelдобавлено полеrarity("uncommon"|"rare"|"epic"|"legendary") - В
UniqueGiftдобавленisBurned - В
UserдобавленallowsUsersToCreateTopics()
@gramio/views — Система шаблонов для переиспользуемых сообщений (НОВОЕ)
4de789b cd3b934 944a5fb c7521ae b2f917b
Свежий пакет для создания переиспользуемых шаблонов сообщений с автоматическим выбором стратегии отправки/редактирования. Определяете вьюшку один раз — библиотека сама решает, отправить новое сообщение или отредактировать текущее, в зависимости от типа контекста.
Программные вьюшки
import { initViewsBuilder } from "@gramio/views";
import { defineAdapter } from "@gramio/views/define";
const adapter = defineAdapter({
welcome(name: string) {
return this.response
.text(`Привет, ${name}!`)
.keyboard([[{ text: "Начать", callback_data: "start" }]]);
},
});
const defineView = initViewsBuilder().from(adapter);
bot.derive(["message", "callback_query"], (context) => ({
render: defineView.buildRender(context, {}),
}));
bot.command("start", (context) => context.render("welcome", "Alice"));Вьюшки из JSON
Определяйте вьюшки как JSON с интерполяцией в тексте, клавиатурах и медиа:
import { createJsonAdapter } from "@gramio/views/json";
const adapter = createJsonAdapter({
views: {
welcome: {
text: "Привет, {{name}}!",
reply_markup: {
inline_keyboard: [
[{ text: "Профиль {{name}}", callback_data: "profile_{{id}}" }],
],
},
},
},
});Загрузка из файловой системы
import { loadJsonViewsDir } from "@gramio/views/fs";
// views/messages.json → "messages.welcome", "messages.goodbye"
// views/goods/products.json → "goods.products.list"
const views = await loadJsonViewsDir("./views");Поддержка i18n
Два подхода: фабрика адаптера для JSON-файлов по локалям, или кастомный resolve для ключей перевода:
// Выбор адаптера по локали
const defineView = initViewsBuilder<{ locale: string }>()
.from((globals) => adapters[globals.locale]);
// Или кастомный resolve для ключей перевода
const adapter = createJsonAdapter({
views: { greet: { text: "{{t:hello}}, {{name}}!" } },
resolve: (key, globals) => {
if (key.startsWith("t:")) return globals.t(key.slice(2));
},
});Поддерживает все типы клавиатур (inline, reply, remove, force reply), одиночные и групповые медиа с интерполяцией URL, а также доступ к глобалам через синтаксис .
@gramio/opentelemetry — Распределённый трейсинг (НОВОЕ)
Вендор-нейтральный трейсинг для GramIO через OpenTelemetry API. Каждый update становится корневым спаном, каждый API-вызов — дочерним. Работает с любым OTEL-бэкендом (Jaeger, Grafana, Axiom и т.д.).
import { opentelemetryPlugin } from "@gramio/opentelemetry";
bot.extend(opentelemetryPlugin({
recordApiParams: true, // записывать параметры API как атрибуты спана
}));Иерархия трейсов:
gramio.update.message (CONSUMER)
├── telegram.api/sendMessage (CLIENT)
├── telegram.api/deleteMessage (CLIENT)
└── кастомные спаны через record()Экспортируемые утилиты: record(name, fn) для кастомных дочерних спанов, getCurrentSpan(), setAttributes(). Бесшовно интегрируется с вебхуками Elysia — спаны GramIO автоматически вкладываются в HTTP-спаны запроса.
@gramio/sentry — Отслеживание ошибок (НОВОЕ)
Интеграция с Sentry: автоматический перехват ошибок, идентификация пользователей, breadcrumbs и опциональный трейсинг:
import { sentryPlugin } from "@gramio/sentry";
bot.extend(sentryPlugin({
setUser: true, // автоматическая привязка пользователя из context.from
breadcrumbs: true, // breadcrumb на каждый update и API-вызов
tracing: false, // isolation scopes + спаны на каждый update
}));
// Методы из контекста:
bot.command("test", (context) => {
context.sentry.captureMessage("Что-то произошло");
context.sentry.setTag("custom", "value");
});Использует @sentry/core для поддержки разных рантаймов (Bun и Node.js).
@gramio/keyboards v1.3.0 — Стилизация кнопок
Все методы кнопок теперь принимают необязательный параметр options для визуальной стилизации:
new InlineKeyboard()
.text("Удалить", "delete", { style: "danger" })
.text("Подтвердить", "confirm", {
style: "success",
icon_custom_emoji_id: "5368324170671202286",
});Три стиля: "danger" (красный), "primary" (синий), "success" (зелёный). Плюс icon_custom_emoji_id для кастомных эмодзи рядом с текстом кнопки. Работает и в InlineKeyboard, и в Keyboard.
@gramio/types v9.4.0
Новые типы для Bot API 9.4: VideoQuality, UserProfileAudios, ChatOwnerLeft, ChatOwnerChanged, UniqueGiftModelRarity. Типы стилизации кнопок (KeyboardButtonStyle, InlineKeyboardButtonStyle) теперь официальные. Новые методы API: getUserProfileAudios, setMyProfilePhoto, removeMyProfilePhoto.
@gramio/storage-sqlite v1.0.0 — Теперь работает на Node.js!
Адаптер SQLite больше не только для Bun! Теперь с двойными экспортами под каждый рантайм:
- Bun: использует
bun:sqlite(без изменений) - Node.js: использует
node:sqliteсDatabaseSync
Нужная реализация подставляется автоматически по рантайму — менять код не нужно.
@gramio/storage-redis — Нативный Redis для Bun
Адаптер Redis теперь поддерживает встроенный RedisClient из Bun наряду с ioredis:
// Автоматически выбирается на Bun — ioredis не нужен
import { redisStorage } from "@gramio/storage-redis";
const storage = redisStorage({ url: "redis://localhost:6379" });Двойные экспорты: @gramio/storage-redis (автоопределение рантайма), @gramio/storage-redis/ioredis (явно), @gramio/storage-redis/bun (явно). Peer-зависимость ioredis теперь опциональна.
@gramio/test — Мокирование API и симуляция чатов
onApi / offApi для подмены ответов API
import { apiError } from "@gramio/test";
// Статичный ответ
env.onApi("sendMessage", { message_id: 1, chat: { id: 1 }, ... });
// Динамический обработчик
env.onApi("getChat", (params) => {
if (params.chat_id === 123) return chatData;
return apiError(400, "Chat not found");
});
// Симуляция ошибки с retry_after
env.onApi("sendMessage", apiError(429, "Too Many Requests", { retry_after: 30 }));
env.offApi("sendMessage"); // сбросить один
env.offApi(); // сбросить всеОбъекты чатов и действия пользователей
const chat = env.createChat();
const user = env.createUser({ first_name: "Alice" });
await user.join(chat); // эмитит chat_member + new_chat_members
await user.sendMessage(chat, "Привет!"); // эмитит message в чате
await user.click("button_data"); // эмитит callback_query
await user.leave(chat); // эмитит chat_member + left_chat_memberВсе API-вызовы записываются в env.apiCalls для проверки в тестах.