Formatting

RichBuilder

A fluent builder for structured rich messages — headings, paragraphs, quotes, tables, dividers, and media in one document. The output is an InputRichMessage you pass to TL methods that accept one.

What it is

Plain SendMessageships a single text body with entity styling. RichBuilder produces a layered document — multiple blocks, embedded media, tables — that renders as an Instant-View-style card. It is the right tool when message structure is determined at runtime and string templating starts to fight you.

Build a *RichBuilder with telegram.NewRichMessage() and chain methods.

Text via Markdown or HTML

The simplest entry point: hand the builder a chunk of Markdown or HTML and let it parse the entity layer for you.

msg := telegram.NewRichMessage().
	Markdown("**Build:** passing\nSee [logs](https://ci.example.com/run/4321)")

raw := msg.Build()
// raw is an InputRichMessage you can pass to a TL method that accepts one.
msg := telegram.NewRichMessage().
	Markdown("**Build:** passing\nSee [logs](https://ci.example.com/run/4321)")

raw := msg.Build()
// raw is an InputRichMessage you can pass to a TL method that accepts one.
msg := telegram.NewRichMessage().
	HTML("<b>Build:</b> passing<br/>See <a href=\"https://ci.example.com/run/4321\">logs</a>")
msg := telegram.NewRichMessage().
	HTML("<b>Build:</b> passing<br/>See <a href=\"https://ci.example.com/run/4321\">logs</a>")

Each call appends to the document. Mixing Markdown and HTMLon the same builder works — both end up as parsed entity lists.

Block-level content

For structured documents, the typed block API is clearer than ad-hoc Markdown:

msg := telegram.NewRichMessage().
	Heading("Quarterly report").
	Paragraph("Revenue is up 14 % quarter-over-quarter.").
	Divider().
	Paragraph("Margins remain pressured by infra costs.").
	Quote("Profit is opinion. Cash is fact.")
msg := telegram.NewRichMessage().
	Heading("Quarterly report").
	Paragraph("Revenue is up 14 % quarter-over-quarter.").
	Divider().
	Paragraph("Margins remain pressured by infra costs.").
	Quote("Profit is opinion. Cash is fact.")
  • Heading(text) — section heading.
  • Paragraph(text) — a normal text block.
  • Quote(text) — pulled quote.
  • Divider() — visual separator.
  • Latex(s) — inline math.
  • AddBlock(block) / Blocks(blocks...) — drop in a hand-built PageBlock.

Adding media

msg := telegram.NewRichMessage().
	Heading("Latest release").
	Paragraph("Cut yesterday, available everywhere now.").
	AddPhoto("./cover.jpg").
	AddDocument("./release-notes.pdf")
msg := telegram.NewRichMessage().
	Heading("Latest release").
	Paragraph("Cut yesterday, available everywhere now.").
	AddPhoto("./cover.jpg").
	AddDocument("./release-notes.pdf")

AddPhoto, AddDocument, AddVideo, AddAudiotake any source — file path, byte slice, reader, URL — and upload it as part of the message build step.

For previously-uploaded media, AttachPhoto and AttachDocument take an existing InputPhoto or InputDocument and skip the upload.

Photo-only multi-image layouts use SlideshowPhotos(photos...) or CollagePhotos(photos...).

Tables and details

rows := [][]any{
	{"Service", "p50", "p99"},
	{"api",     "12ms", "85ms"},
	{"worker",  "8ms",  "32ms"},
}

msg := telegram.NewRichMessage().
	Heading("Latency by service").
	Table(rows, &telegram.TableOptions{
		Bordered:     true,
		HasHeader:    true,
	})
rows := [][]any{
	{"Service", "p50", "p99"},
	{"api",     "12ms", "85ms"},
	{"worker",  "8ms",  "32ms"},
}

msg := telegram.NewRichMessage().
	Heading("Latency by service").
	Table(rows, &telegram.TableOptions{
		Bordered:     true,
		HasHeader:    true,
	})

Table(rows, opts) takes a 2D slice of values (strings, ints, or pre-built rich-text fragments). Options control borders, header row, alignment.

Details(title, blocks...) creates a collapsed/expandable section:

msg := telegram.NewRichMessage().
	Paragraph("The release went out at 14:32 UTC.").
	Details("Show change log",
		&telegram.PageBlockParagraph{
			Text: &telegram.TextPlain{Text: "Fixed flaky DC reconnect timer."},
		},
	)
msg := telegram.NewRichMessage().
	Paragraph("The release went out at 14:32 UTC.").
	Details("Show change log",
		&telegram.PageBlockParagraph{
			Text: &telegram.TextPlain{Text: "Fixed flaky DC reconnect timer."},
		},
	)

Builder flags

msg := telegram.NewRichMessage().
	RTL().              // right-to-left layout hint
	NoAutoLink().        // do not auto-detect URLs in plain text
	Paragraph("…")
msg := telegram.NewRichMessage().
	RTL().              // right-to-left layout hint
	NoAutoLink().        // do not auto-detect URLs in plain text
	Paragraph("…")
RTL()
Mark the message as right-to-left for layout purposes.
NoAutoLink()
Disable auto-detection of URLs and mentions inside plain text blocks.

Building and using

Build() resolves any pending media uploads and returns an InputRichMessage. Pass it to the matching TL method — the high-level wrapper helpers for "send a rich message" vary across gogram releases, so check the source for the current entry point in your version.