Wiki
猫Rich Messages
Rich messages provide headings, paragraphs, lists, tables, media, quotations, expandable sections, and formulas. Laniakea supports outgoing InputRichBlock trees from Bot API 10.2, incoming RichBlock trees, HTML conversion, multipart uploads, and streaming drafts.
Packages and data flow
tgapicontains Telegram wire types and API methods.tgrichcontains constructors fortgapi.RichTextandtgapi.InputRichBlock, validation, and HTML conversion.- Outgoing messages use
tgapi.InputRichMessage. Exactly one ofBlocks,HTML, orMarkdownmust be set. - Incoming messages use
Message.RichMessagewithtgapi.RichBlockandtgapi.RichTextnodes.
Outgoing and incoming block types are intentionally separate. InputRichBlock* describes data accepted by Telegram; RichBlock* describes the normalized tree returned by Telegram.
Sending from a handler
MessageContext.RichAnswer accepts input blocks and builds a validated HTML rich message:
import (
"git.scuroneko.dev/scuroneko/laniakea/tgrich"
)
func report(ctx *laniakea.MessageContext, _ struct{}) error {
ctx.RichAnswer(
tgrich.H1(tgrich.Text("Daily report")),
tgrich.P(tgrich.Concat(
tgrich.Text("Status: "),
tgrich.Bold(tgrich.Text("all systems go")),
)),
tgrich.Ul(
tgrich.NewListItem(tgrich.P(tgrich.Text("backups"))).
SetCheckbox().SetChecked().Build(),
tgrich.NewListItem(tgrich.P(tgrich.Text("migration"))).
SetCheckbox().Build(),
),
)
return nil
}
RichAnswerKeyboard(keyboard, blocks...) sends the same content with an inline keyboard.
Building text and blocks
Inline text constructors include Text, Concat, Bold, Italic, Underline, Strikethrough, Spoiler, Code, Marked, Subscript, Superscript, URL, Email, Phone, TextMention, Mention, Hashtag, Cashtag, BotCommand, Emoji, DateTime, MathExpression, anchors, and references.
Block constructors include P, H1-H6, Pre, CodeBlock, Footer, Hr, Math, Anchor, Ul, Ol, quotations, collages, slideshows, tables, details, maps, animation, audio, photo, video, voice notes, and the draft-only Thinking block.
List items and tables use small builders:
ordered := tgrich.Ol(
tgrich.OlOpts{Start: 3, Type: tgapi.InputRichBlockListItemTypeLower},
tgrich.NewListItem(tgrich.P(tgrich.Text("third"))).Build(),
)
table := tgrich.NewTable(
tgrich.Row(
tgrich.CellWithText(tgrich.Text("Name")).SetHeader().Build(),
tgrich.CellWithText(tgrich.Text("Value")).SetHeader().Build(),
),
tgrich.Row(
tgrich.CellWithText(tgrich.Text("Status")).Build(),
tgrich.CellWithText(tgrich.Bold(tgrich.Text("OK"))).Build(),
),
).SetBordered(true).Build()
Block payloads and HTML conversion
Send the input tree directly when no conversion is needed:
rich := tgapi.InputRichMessage{
Blocks: []tgapi.InputRichBlock{
tgrich.H1(tgrich.Text("Report")),
tgrich.P(tgrich.Text("Ready")),
},
}
Use tgrich.BuildHTML(blocks...) to validate a whole tree and convert it to an HTML-based InputRichMessage. tgrich.ToHTML(block) is the single-block convenience form. Conversion is useful for logging, previews, or APIs such as MessageContext.RichAnswer that send rendered HTML.
BuildHTML enables SkipEntityDetection, escapes text and attributes, and enforces Telegram's rich-message limits:
- 32768 UTF-8 characters;
- 500 blocks, including nested blocks, list items, and table rows;
- 16 combined levels of nested blocks and formatting;
- 50 media attachments;
- 20 table columns, including
colspan.
It also validates block discriminators, heading sizes, list marker and checkbox state, table alignment and spans, map coordinates/zoom/dimensions, and media types.
Media and multipart uploads
Media constructors accept tgapi.InputMedia, so Media can contain an HTTP URL, Telegram file_id, or an attach://name reference:
block := tgrich.PhotoWithCaption(
tgapi.InputMedia{Media: "telegram-file-id"},
tgrich.CaptionWithCredit(
tgrich.Text("Launch"),
tgrich.Text("Operations"),
),
)
During HTML conversion, media blocks become tg://photo?id=..., tg://video?id=..., or tg://audio?id=... references. The original InputMedia values are collected in InputRichMessage.Media under stable media_1, media_2, ... identifiers.
For multipart uploads, the attach:// name and multipart field name must match:
rich, err := tgrich.BuildHTML(
tgrich.Photo(tgapi.InputMedia{Media: "attach://report"}),
)
if err != nil {
return err
}
_, err = uploader.SendRichMessage(
tgapi.SendRichMessage{ChatID: chatID, RichMessage: rich},
tgapi.NewUploaderFile("report.jpg", data).SetAttachName("report"),
)
The same upload model is available through Uploader.SendRichMessageDraft.
Direct API access
API.SendRichMessagesends a completed rich message.API.SendRichMessageDraftupdates an ephemeral preview while content is generated.Thinkingis valid only in drafts.Uploader.SendRichMessageandUploader.SendRichMessageDraftupload files referenced throughattach://.EditMessageText.RichMessageedits an existing rich message; leaveTextempty.InputRichMessageContentsupplies rich content for inline results.
Receiving
Message.RichMessage is parsed automatically. Walk the received tree with type switches:
for _, block := range msg.RichMessage.Blocks {
switch b := block.(type) {
case tgapi.RichBlockWrap: // paragraph, footer, thinking
handleText(b.Text)
case tgapi.RichBlockList:
for _, item := range b.Items {
// Label is the server-rendered marker: "1.", "c.", "vii.", or "•".
}
}
}
Unknown future nodes with a text field are preserved as RichTextWrap or RichBlockWrap, allowing parsers to tolerate compatible Bot API additions.
Pitfalls
InputRichBlock*and receivedRichBlock*are different type families.Thinkingcan be sent only throughsendRichMessageDraft.- A checked list item must also have a checkbox.
- Ordered items require one of the supported marker types; unordered items must not set
Value. - Table width includes column spans.
BuildHTMLreturns validation errors before making an API request; useerrors.Iswith the exportedtgrich.ErrRich*values when callers need to classify them.
Where to go next
- MessageContext - handler response helpers.
- tgapi-Overview - low-level API client.
- Drafts - staged message generation.
Navigation
Start here
Runtime and Architecture
- Bot-Lifecycle
- Webhook-Runtime
- Middleware
- Runners
- Error-Handling
- Logging
- Update-Routing-Model
- Policies
- Scenes
Interaction and Telegram API
- Inline-Keyboards-and-Payloads
- Auto-Generated-Commands
- Drafts
- Rich-Messages
- Localization
- Rate-Limiting
- tgapi-Overview