Text Formatting¶
Irenogram supports the same text formatting features available in Telegram. You can format text using either Markdown or HTML syntax.
Parse Modes¶
Irenogram supports three parse modes:
|
You can set the parse mode per-message or globally via the parse_mode parameter.
Markdown Style¶
from pyrogram import Client
from pyrogram.enums import ParseMode
app = Client("my_account")
async def main():
async with app:
await app.send_message(
"me",
(
"**bold text**\n"
"__italic text__\n"
"~~strikethrough~~\n"
"||spoiler text||\n"
"`inline code`\n"
"```block code```\n"
"[text link](https://irenogram.org)\n"
"[inline mention](tg://user?id=123456789)"
),
parse_mode=ParseMode.MARKDOWN
)
import asyncio
asyncio.run(main())
HTML Style¶
from pyrogram import Client
from pyrogram.enums import ParseMode
app = Client("my_account")
async def main():
async with app:
await app.send_message(
"me",
(
"<b>bold text</b>\n"
"<i>italic text</i>\n"
"<s>strikethrough</s>\n"
"<spoiler>spoiler text</spoiler>\n"
"<code>inline code</code>\n"
"<pre>block code</pre>\n"
'<a href="https://irenogram.org">text link</a>\n'
'<a href="tg://user?id=123456789">inline mention</a>'
),
parse_mode=ParseMode.HTML
)
import asyncio
asyncio.run(main())
Setting a Default Parse Mode¶
You can set a default parse mode for the entire client so you don’t have to specify it on every method call:
from pyrogram import Client
from pyrogram.enums import ParseMode
app = Client("my_account", parse_mode=ParseMode.HTML)
async def main():
async with app:
await app.send_message("me", "<b>Hello!</b>")
await app.send_message("me", "**Hello!**", parse_mode=ParseMode.MARKDOWN)
import asyncio
asyncio.run(main())
Disabling Parse Mode¶
To send raw text without any formatting, set the parse mode to None or ParseMode.DISABLED:
from pyrogram.enums import ParseMode
await app.send_message(
"me",
"This **text** will appear literally with asterisks",
parse_mode=ParseMode.DISABLED
)
Nested and Combined Formatting¶
Telegram supports nested formatting:
await app.send_message(
"me",
"**bold __bold italic__ bold**",
parse_mode=ParseMode.MARKDOWN
)
Tip
When in doubt, HTML formatting tends to be more predictable than Markdown, especially when dealing with special characters or nested formatting.
Custom Emoji¶
Telegram supports custom emoji in messages. Custom emoji are premium stickers that can be used inline
within text. They are represented by the <tg-emoji> HTML tag or the MessageEntityType.CUSTOM_EMOJI
entity type.
Using Custom Emoji with HTML¶
Use the <tg-emoji> tag with the emoji-id attribute pointing to the custom emoji’s document ID:
from pyrogram import Client
from pyrogram.enums import ParseMode
app = Client("my_account")
async def main():
async with app:
await app.send_message(
"me",
'<tg-emoji emoji-id="5368324170671202286">👍</tg-emoji> Great job!',
parse_mode=ParseMode.HTML
)
import asyncio
asyncio.run(main())
The fallback emoji (between the tags) is displayed on clients that do not support custom emoji.
Using Custom Emoji with Markdown¶
Markdown also supports inline custom emoji using the tg://emoji URI scheme:
from pyrogram import Client
from pyrogram.enums import ParseMode
app = Client("my_account")
async def main():
async with app:
await app.send_message(
"me",
" On fire!",
parse_mode=ParseMode.MARKDOWN
)
import asyncio
asyncio.run(main())
The syntax is . The fallback emoji
is shown where custom emoji cannot be rendered.
Using Custom Emoji with MessageEntity¶
You can also send custom emoji by constructing message entities manually:
from pyrogram import Client
from pyrogram.types import MessageEntity
from pyrogram.enums import MessageEntityType
app = Client("my_account")
async def main():
async with app:
await app.send_message(
"me",
"👍 Great job!",
entities=[
MessageEntity(
type=MessageEntityType.CUSTOM_EMOJI,
offset=0,
length=2,
custom_emoji_id=5368324170671202286
)
]
)
import asyncio
asyncio.run(main())
Retrieving Custom Emoji Stickers¶
To get information about custom emoji stickers by their IDs:
stickers = await app.get_custom_emoji_stickers(
custom_emoji_ids=[5368324170671202286, 5368324170671202287]
)
for sticker in stickers:
print(sticker.emoji, sticker.file_id)
Note
Custom emoji entities can only be used by bots that purchased additional usernames on Fragment, or in messages sent directly by the bot to private, group, and supergroup chats if the owner of the bot has a Telegram Premium subscription.
Custom emoji cannot be used in channel messages.
A valid fallback emoji must always be provided as the content of the <tg-emoji> tag (or as
the Markdown fallback). It will be shown instead of the custom emoji where custom emoji cannot
be displayed — for example in system notifications or when a message is forwarded by a
non-premium user. It is recommended to use the emoji from the emoji field of the custom
emoji sticker.
Non-premium users can see custom emoji but cannot use them in their own messages unless the emoji is from a free emoji pack.
HTML Formatting Notes¶
Telegram’s HTML parse mode has a limited set of supported tags. Keep these rules in mind:
Only the tags documented above are currently supported.
All
<,>and&symbols that are not part of a tag or HTML entity must be escaped:<→<,>→>,&→&.All numerical HTML entities are supported.
The API currently supports only the following named HTML entities:
<,>,&and".Use nested
<pre>and<code>tags to define programming language for a pre block. Programming language cannot be specified for standalone<code>tags.
Scheduling Messages with schedule_date¶
All send methods accept an optional schedule_date parameter (a datetime object) to schedule
a message for future delivery:
from datetime import datetime, timezone
scheduled_time = datetime(2025, 12, 31, 23, 59, 0, tzinfo=timezone.utc)
await app.send_message(
"me",
"Happy New Year!",
schedule_date=scheduled_time
)
The schedule_date parameter is available on:
Telegram Timestamp (tg-time) — HTML Formatting¶
Telegram’s HTML parse mode supports a special <tg-time> tag that renders a Unix timestamp as a
locale-aware, interactive time element in the Telegram client. The timestamp is specified via the
unix attribute and the display format is controlled via the format attribute.
Supported format values¶
|
Description |
Example output |
|---|---|---|
|
Short time (hours and minutes) |
|
|
Long time (hours, minutes, and seconds) |
|
|
Short date |
|
|
Long date |
|
|
Short date and time (default when omitted) |
|
|
Long date and time with weekday |
|
|
Relative time |
|
|
Weekday + short date + time |
|
Usage examples¶
The fallback text (between the opening and closing tags) is displayed in clients that do not support
the <tg-time> tag.
<tg-time unix="1647531900" format="t">22:45 tomorrow</tg-time>
<tg-time unix="1647531900" format="wDT">22:45 tomorrow</tg-time>
<tg-time unix="1647531900" format="r">22:45 tomorrow</tg-time>
<tg-time unix="1647531900">22:45 tomorrow</tg-time>
Sending a tg-time message with Irenogram¶
import time
from pyrogram import Client
from pyrogram.enums import ParseMode
app = Client("my_account")
async def main():
unix_ts = int(time.time()) + 7200
text = (
f'Meeting starts at: <tg-time unix="{unix_ts}" format="t">{unix_ts}</tg-time>\n'
f'Date: <tg-time unix="{unix_ts}" format="D">{unix_ts}</tg-time>\n'
f'Relative: <tg-time unix="{unix_ts}" format="r">{unix_ts}</tg-time>'
)
async with app:
await app.send_message("me", text, parse_mode=ParseMode.HTML)
import asyncio
asyncio.run(main())
Note
The <tg-time> tag is only rendered by official Telegram clients. Other clients display the
fallback text between the tags. See the format table above for supported format values.
Refer to Telegram’s date-time entity formatting documentation for further details.