Skip to main content

Styled text and HTML

Telegram represents formatting as a list of message entities (offset, length, type) alongside the plain text. Building those by hand is error-prone, so gotd offers two approaches: a typed styling DSL and an HTML parser.

The styling package​

message/styling provides one function per entity type. Pass them to StyledText:

import "github.com/gotd/td/telegram/message/styling"

sender.Resolve("@channel").StyledText(ctx,
styling.Plain("Normal, "),
styling.Bold("bold, "),
styling.Italic("italic, "),
styling.Code("monospace"),
styling.Plain(" and a "),
styling.TextURL("link", "https://gotd.dev"),
)

Available options include Plain, Bold, Italic, Underline, Strike, Code, Pre(text, language), Spoiler, Blockquote(text, collapsed), TextURL(text, url), URL, Mention, MentionName(text, user), Hashtag, Cashtag, BotCommand, Email, Phone, BankCard and CustomEmoji(text, documentID).

The same options are used as captions when sending media, and by inline results.

HTML​

If you already have HTML (for example from a templating layer), the message/html package parses Telegram's supported HTML subset into a styled option:

import "github.com/gotd/td/telegram/message/html"

sender.Self().StyledText(ctx,
html.String(nil, `Upload: <b>done</b> — <i>see attached</i>`),
)

The first argument is an optional resolver for tg://user?id=... mention links; pass nil when you don't use them. html.Format(...) accepts a format string.

Inline keyboards​

Buttons are built with the message/markup package and attached with Markup:

import "github.com/gotd/td/telegram/message/markup"

sender.Resolve("@user").Markup(
markup.InlineKeyboard(
markup.Row(
markup.Callback("Yes", []byte("yes")),
markup.Callback("No", []byte("no")),
),
),
).Text(ctx, "Proceed?")

Handle presses with dispatcher.OnBotCallbackQuery (see Handling updates).

Inline bot results​

For inline bots, the message/inline package builds answers, and result messages reuse styling:

import "github.com/gotd/td/telegram/message/inline"

dispatcher.OnBotInlineQuery(func(ctx context.Context, e tg.Entities, u *tg.UpdateBotInlineQuery) error {
_, err := inline.New(api, rand.Reader, u.QueryID).
Set(ctx,
inline.Article("Styled greeting",
inline.MessageStyledText(
styling.Bold("Hello"),
styling.Plain(", "),
styling.Italic(u.Query),
),
).ID("greeting").Description("Send a styled greeting"),
)
return err
})

This is the bot-inline example.

Rich (page-block) messages​

For instant-view-style structured content — titles, headers, paragraphs, lists, dividers — the message/rich package builds RichMessage payloads, as shown in rich-message:

import "github.com/gotd/td/telegram/message/rich"

msg := rich.New(
rich.Title(rich.Plain("gotd")),
rich.Paragraph(rich.Concat(
rich.Plain("Mixing "), rich.Bold(rich.Plain("bold")),
rich.Plain(" and "), rich.Italic(rich.Plain("italic")), rich.Plain("."),
)),
rich.List(
rich.ListItem(rich.Plain("first")),
rich.ListItem(rich.Plain("second")),
),
).Input()

sender.Self().RichMessage(ctx, msg)