Rich Messages
Rich Messages добавляют блочную разметку поверх обычных text и MessageEntity[]: заголовки, списки, документы, ряды кнопок, раскрываемые цитаты, таблицы, медиаколлекции и потоковые черновики. GramIO предоставляет три уровня:
- Хелперы
@gramio/format/richдля безопасной композиции. @gramio/jsx/richдля rich JSX.- Сырые структуры
bot.api.sendRichMessage()для полного контроля и загрузок.
Сборка rich-сообщения
import { Bot, bold, format } from "gramio";
import {
button,
buttonRow,
document,
heading,
paragraph,
quote,
rich,
table,
} from "gramio/rich";
const bot = new Bot(process.env.BOT_TOKEN as string);
bot.command("report", (ctx) =>
ctx.send(
rich([
heading(1, "Отчёт о релизе"),
paragraph(format`Статус: ${bold`готово`}`),
quote("Подробности можно раскрыть", {
expandable: true,
credit: "Система сборки",
}),
document({
url: "https://example.com/report.pdf",
caption: "Полный отчёт",
}),
table(
[
["Пакет", "Версия"],
["gramio", "0.14.0"],
],
{ compact: true, align: ["left", "right"] },
),
buttonRow(
[
button("Открыть", { type: "url", url: "https://gramio.dev" }),
button("Обновить", {
type: "callback_data",
data: "refresh-report",
}),
button("Скоро", { type: "disabled" }),
],
{ align: "right" },
),
]),
),
);Хелперы экранируют пользовательские строки. Не собирайте raw rich Markdown/HTML конкатенацией вокруг недоверенных данных.
Rich JSX
Используйте отдельный JSX import source, чтобы обычный форматирующий JSX и rich JSX не смешивались:
/** @jsxImportSource @gramio/jsx/rich */
const message = (
<rich>
<h1>Отчёт о релизе</h1>
<blockquote expandable credit="Система сборки">
Подробности можно раскрыть
</blockquote>
<document url="https://example.com/report.pdf" caption="Полный отчёт" />
<table compact align={["left", "right"]}>
<tr><th>Пакет</th><th>Версия</th></tr>
<tr><td>gramio</td><td>0.14.0</td></tr>
</table>
<button-row align="right">
<button type="url" url="https://gramio.dev">Открыть</button>
<button type="disabled">Скоро</button>
</button-row>
</rich>
);Передайте message в ctx.send() так же, как значение от rich().
Загрузка файлов внутри rich-сообщения
@gramio/files рекурсивно находит загрузки в rich_message.blocks и rich_message.media, включая вложенные документы, миниатюры и обложки. Middleware заменяет каждую загрузку ссылкой attach://….
import { Bot, MediaUpload } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string);
bot.command("upload-report", async (ctx) => {
const report = await MediaUpload.path("./report.pdf");
return bot.api.sendRichMessage({
chat_id: ctx.chatId,
rich_message: {
html: '<tg-document src="tg://document?id=report"></tg-document>',
media: [
{
id: "report",
media: { type: "document", media: report },
},
],
},
});
});Документ можно поместить и напрямую в rich_message.blocks как InputRichBlockDocument; рекурсивные загрузки там тоже поддерживаются.
Потоковый черновик
Draft ID — заданное приложением ненулевое число. Повторное использование ID анимирует следующий частичный результат. Черновик временный: после завершения отправьте итог через sendRichMessage.
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string);
await bot.api.sendRichMessageDraft({
chat_id: 42,
draft_id: 1001,
rich_message: { markdown: "## Генерация…" },
can_stop: true,
keep_on_stop: true,
});В черновиках нет прямых загрузок
Telegram запрещает прямую загрузку файлов в sendRichMessageDraft, поэтому GramIO исключает этот метод из upload extraction. Переиспользуйте существующий file_id; не передавайте MediaUpload, Blob или новый URL для загрузки.
Обработка остановки генерации
Если включён can_stop и пользователь нажал Stop, Telegram отправляет stopped_message_generation. Обновление входит в стандартный набор GramIO — ручной opt-in не нужен.
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string);
bot.on("stopped_message_generation", async (ctx) => {
console.log(ctx.draftId, ctx.threadId, ctx.chatId, ctx.chatType);
await ctx.send("Генерация остановлена.");
});ctx.draftId идентифицирует остановленный черновик. ctx.threadId присутствует в теме, а последующая отправка автоматически сохраняет эту тему.