telegramify

package module
v0.0.0-...-c2f204e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 23, 2026 License: MIT Imports: 10 Imported by: 0

README

telegramify-markdown-go

Go Reference test Go Report Card

Convert standard Markdown into the MarkdownV2 dialect understood by the Telegram Bot API.

Telegram's MarkdownV2 supports only a small subset of Markdown and requires many ASCII punctuation characters (_ * [ ] ( ) ~ ` > # + - = | { } . !) to be backslash-escaped — getting any of it wrong makes the Bot API reject your message with 400 Bad Request. This library takes arbitrary Markdown, escapes it correctly, and maps unsupported constructs (headings, tables, images, task lists, …) onto the closest Telegram-compatible representation, so you can just send the result with parse_mode = "MarkdownV2".

This is a Go port of the Python library telegramify-markdown, covering its core Markdown → MarkdownV2 string conversion plus message splitting. Parsing is done with goldmark.

Install

go get github.com/barbashov/telegramify-markdown-go

Requires Go 1.22+.

Quick start

package main

import (
	"fmt"

	telegramify "github.com/barbashov/telegramify-markdown-go"
)

func main() {
	md := "## Shopping list\n\nDon't forget **milk** and `eggs`! Cost: $3.50."
	out := telegramify.Markdownify(md)
	fmt.Println(out)
	// ✏️ *__Shopping list__*
	//
	// Don't forget *milk* and `eggs`\! Cost: $3\.50\.
}

Then send it with your Telegram bot library of choice using parse_mode = "MarkdownV2". For example, with go-telegram-bot-api:

msg := tgbotapi.NewMessage(chatID, telegramify.Markdownify(text))
msg.ParseMode = tgbotapi.ModeMarkdownV2
bot.Send(msg)

Splitting long messages

Telegram rejects messages longer than 4096 UTF-16 code units. Split breaks a rendered string into chunks that each fit, cutting at newline boundaries so markup is not broken across chunks (as long as each block is itself under the limit):

out := telegramify.Markdownify(longText)
for _, chunk := range telegramify.Split(out, telegramify.DefaultMaxLength) {
	msg := tgbotapi.NewMessage(chatID, chunk)
	msg.ParseMode = tgbotapi.ModeMarkdownV2
	bot.Send(msg)
}

Split measures length in UTF-16 code units — the same unit Telegram uses — so emoji and other astral-plane characters are counted correctly and never cut mid-rune.

Supported Markdown

Markdown Rendered as
**bold** *bold*
_italic_ / *italic* _italic_
~~strike~~ ~strike~
`code` `code` (only ` and \ escaped inside)
```lang … ``` fenced code block, language preserved
[text](url) [text](url) (only ) and \ escaped in the URL)
![alt](url) link with an image prefix, e.g. [🖼 alt](url)
![👍](tg://emoji?id=…) Telegram custom emoji (kept as ![…](…))
# H1###### H6 emoji prefix + bold/underline/italic styling per level
- item / 1. item • item / 1\. item (with nesting)
- [x] done task list with ✅ / ☐ markers
> quote blockquote; long quotes become expandable
tables aligned monospace block
--- a horizontal-rule string (————————)
||spoiler|| ||spoiler|| (Telegram spoiler)

Bare URLs in text are not auto-linkified — they are left as plain (escaped) text, matching how most chat content is authored.

Configuration

Markdownify accepts functional options:

out := telegramify.Markdownify(md,
	telegramify.WithHeadingSymbols("➤"),          // prefixes for H1..H6
	telegramify.WithImageSymbol("🏞"),             // prefix for image links
	telegramify.WithHorizontalRule("──────"),     // thematic break string
	telegramify.WithTaskSymbols("✔", "▢"),        // done / todo markers
	telegramify.WithUnorderedMarker("·"),         // bullet for unordered lists
	telegramify.WithCiteExpandable(false),        // disable expandable blockquotes
	telegramify.WithExpandableThreshold(120),     // length (UTF-16) to expand at
)

Scope

This port focuses on the most widely used part of the original library: direct Markdown → MarkdownV2 string conversion (Python's markdownify) and message splitting. The Python library's entity-based output (convert) and the async pipeline (telegramify with code-file extraction and Mermaid image rendering) are out of scope for this release.

License

MIT. Port of telegramify-markdown (© 2024 Jasmine), which is also MIT-licensed.

Documentation

Overview

Package telegramify converts standard (CommonMark/GFM) Markdown into the MarkdownV2 dialect understood by the Telegram Bot API.

Telegram's MarkdownV2 supports only a small subset of Markdown and requires many ASCII punctuation characters to be backslash-escaped; getting this wrong makes the Bot API reject the message. This package handles the escaping and maps unsupported constructs (headings, tables, images, task lists, ...) onto the closest Telegram-compatible representation, so you can feed it arbitrary Markdown and send the result with parse_mode = "MarkdownV2".

It is a Go port of the Python library telegramify-markdown (https://github.com/sudoskys/telegramify-markdown), covering the core Markdown -> MarkdownV2 string conversion and message splitting.

Basic usage:

text := telegramify.Markdownify("# Title\n\nHello **world**!")
// send text with parse_mode = "MarkdownV2"

Index

Examples

Constants

View Source
const DefaultMaxLength = 4096

DefaultMaxLength is Telegram's maximum message length in UTF-16 code units.

Variables

This section is empty.

Functions

func Markdownify

func Markdownify(markdown string, opts ...Option) string

Markdownify converts a Markdown string into Telegram MarkdownV2.

The result is safe to send to the Telegram Bot API with parse_mode = "MarkdownV2". Rendering can be tuned with Options such as WithHeadingSymbols or WithCiteExpandable.

Example
package main

import (
	"fmt"

	telegramify "github.com/barbashov/telegramify-markdown-go"
)

func main() {
	md := "## Shopping list\n\nDon't forget **milk** and `eggs`! Cost: $3.50."
	fmt.Println(telegramify.Markdownify(md))
}
Output:
✏️ *__Shopping list__*

Don't forget *milk* and `eggs`\! Cost: $3\.50\.
Example (Options)
package main

import (
	"fmt"

	telegramify "github.com/barbashov/telegramify-markdown-go"
)

func main() {
	out := telegramify.Markdownify("# Title", telegramify.WithHeadingSymbols("➤"))
	fmt.Println(out)
}
Output:
➤ *__Title__*

func Split

func Split(text string, limit int) []string

Split breaks text into chunks that each fit within limit UTF-16 code units (the unit Telegram measures message length in). If limit is <= 0, DefaultMaxLength is used.

Splitting happens at newline boundaries so that, as long as every individual block is shorter than the limit, no MarkdownV2 markup is broken across chunks. A single line longer than the limit is hard-split at rune boundaries as a last resort, which may break markup — keep individual blocks below the limit to avoid that.

Example
package main

import (
	"fmt"

	telegramify "github.com/barbashov/telegramify-markdown-go"
)

func main() {
	long := "first line\nsecond line\nthird line"
	for _, chunk := range telegramify.Split(long, 22) {
		fmt.Printf("%q\n", chunk)
	}
}
Output:
"first line\nsecond line"
"third line"

Types

type Option

type Option func(*config)

Option configures how Markdownify renders Markdown into Telegram MarkdownV2.

func WithCiteExpandable

func WithCiteExpandable(expandable bool) Option

WithCiteExpandable controls whether long blockquotes render as Telegram expandable blockquotes.

func WithExpandableThreshold

func WithExpandableThreshold(units int) Option

WithExpandableThreshold sets the blockquote length, in UTF-16 code units, above which a blockquote becomes expandable. Has no effect unless WithCiteExpandable is enabled (the default).

func WithHeadingSymbols

func WithHeadingSymbols(symbols ...string) Option

WithHeadingSymbols overrides the prefixes used for headings. Up to six symbols may be supplied, mapping to heading levels 1..6; missing levels keep their default. Pass an empty string to render a level with no prefix.

func WithHorizontalRule

func WithHorizontalRule(rule string) Option

WithHorizontalRule sets the string emitted for a Markdown thematic break.

func WithImageSymbol

func WithImageSymbol(symbol string) Option

WithImageSymbol sets the prefix used for the link that replaces an image.

func WithTaskSymbols

func WithTaskSymbols(done, todo string) Option

WithTaskSymbols sets the markers used for completed and pending task list items.

func WithUnorderedMarker

func WithUnorderedMarker(marker string) Option

WithUnorderedMarker sets the bullet used for unordered list items.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL