· 7 min read

Telegram + AI: Using Message Entities to Build Smarter Bots and Better UX

Most Telegram AI bots treat incoming text as plain strings and then struggle with formatting, links, mentions, and commands. This tutorial shows how to use Telegram message entities as a first-class input layer so your bot can route, parse, and respond more reliably.

Most Telegram AI bot tutorials start with /start, a text prompt, and a call to an LLM. That works for demos, but it ignores one of Telegram’s most useful and overlooked features: message entities.

Entities are Telegram’s structured metadata for text. They tell you what part of a message is a mention, hashtag, URL, bot command, code block, bold text, blockquote, and more. If you are building AI features on Telegram, entities let you move beyond brittle string matching and create bots that understand intent, preserve formatting, and react to rich input correctly.

This tutorial focuses on how Telegram developers can use message entities as a practical input layer for AI bots.


Why message entities matter for AI bots

If you read Telegram messages as plain text, you lose important structure:

  • @username becomes just text, even though it may be a target mention
  • /summarize is not clearly separated from the rest of the user input
  • inline code and code blocks get flattened
  • URLs, hashtags, and mentions are hard to classify reliably
  • formatting such as bold or italic may be useful for intent extraction

For AI workflows, that structure is valuable. You can use it to:

  • route commands before sending content to an LLM
  • preserve code blocks when generating explanations
  • detect whether a message includes a mention, URL, or file reference
  • extract user intent from a formatted prompt
  • create hybrid command + freeform interfaces

The result is a bot that feels more native to Telegram and less like a generic chat wrapper.


What Telegram message entities are

Telegram entities are typed spans in a message. Each entity includes:

  • type
  • offset
  • length

Depending on the client and API method, you may also see extra fields such as url or language hints.

Common entity types include:

  • bold
  • italic
  • underline
  • strikethrough
  • spoiler
  • code
  • pre
  • text_link
  • text_mention
  • url
  • mention
  • hashtag
  • cashtag
  • bot_command
  • email
  • phone_number
  • blockquote
  • expandable_blockquote
  • custom_emoji

For AI bots, the most useful categories are usually:

  1. Commands: bot_command
  2. User targets: mention, text_mention
  3. Links and references: url, text_link
  4. Code: code, pre
  5. Formatting: bold, italic, spoiler, blockquote

The overlooked use case: entity-aware prompts

A common mistake is this:

  1. Receive message text
  2. Strip everything to plain text
  3. Send it to the LLM
  4. Hope the model infers intent correctly

That approach makes bots fragile.

Instead, treat Telegram entities as part of the prompt input. Use them to build a structured representation of the message before it reaches your AI layer.

Example user message:

/summarize Please review this code:\n

```js

const x = 2;

console.log(x)

```

If you only pass the raw string, your model sees one blob of text. If you pass structure, your application can tell the model:

  • the message starts with a command
  • there is a code block
  • the user wants a summary, not a rewrite

That one change improves routing, token efficiency, and output quality.


Parsing entities in practice

The exact code depends on your Telegram library, but the logic is the same.

Step 1: read both text and entities

When a message arrives, inspect:

  • message.text
  • message.entities

If the message is formatted, also check:

  • message.caption
  • message.caption_entities

for media messages with attached text.

Step 2: reconstruct the message as structured segments

Instead of flattening the message, build segments like:

  • plain text
  • command
  • mention
  • code block
  • link
  • emphasized text

A simple normalized shape might look like this:

{
  "text": "/summarize Please review this code:",
  "entities": [
    { "type": "bot_command", "offset": 0, "length": 10 },
    { "type": "pre", "offset": 33, "length": 31 }
  ]
}

Step 3: derive intent before calling the model

Use entities for deterministic routing:

  • if the first entity is bot_command, route to a command handler
  • if there is a pre entity, consider code analysis mode
  • if the message contains text_link, extract the URL and process it separately
  • if the message is a reply and includes mentions, combine context with thread state

This reduces the amount of work your model has to do.


Example: building a /summarize bot that respects code blocks

Suppose you want a bot that summarizes text and code.

Bad version

The bot sends the entire message to the model as plain text:

prompt = f"Summarize this message: {message.text}"

Problems:

  • command text is mixed into the user content
  • code may be interpreted as prose
  • formatting is lost
  • long messages may be harder to segment

Better version

Use entities to split command and content.

Pseudo-code:

def extract_command_and_body(message):
    text = message.text or ""
    entities = message.entities or []

    command = None
    body = text

    for e in entities:
        if e.type == "bot_command" and e.offset == 0:
            command = text[e.offset:e.offset + e.length]
            body = text[e.offset + e.length:].lstrip()
            break

    return command, body, entities

Then build a structured prompt:

command, body, entities = extract_command_and_body(message)

payload = {
    "task": "summarize",
    "user_text": body,
    "entities": [
        {
            "type": e.type,
            "offset": e.offset,
            "length": e.length
        }
        for e in entities
    ]
}

Now your LLM receives both the text and the formatting map.

A strong prompt instruction might be:

Summarize the user message. Preserve code semantics. If a code block is present, explain its purpose separately from the surrounding prose.

That gives the model useful constraints without forcing it to infer structure from raw text.


Example: extracting mentions for AI-assisted moderation

If you run a group bot, mentions are more than decoration.

A user may write:

Please ask @alex to check this.

or

Ping Alex about the deployment.

The first contains a structured mention entity. The second does not.

That distinction matters if you are building moderation, routing, or delegation flows.

Use case

Your bot can detect when a message is explicitly directed at a user and:

  • create a task for that user
  • attach the message to a thread
  • notify a downstream workflow
  • avoid misclassifying ordinary text as a target instruction

Important caveat

Do not assume every mention entity maps to a real chat member. The entity may refer to a username, a text mention, or a user not present in the current chat context. Handle that gracefully.


Example: preserving code blocks for coding assistants

This is the most practical AI use case for entities.

If your bot helps with debugging, code review, or architecture questions, you should treat code entities as first-class data.

Why?

Because code blocks change how the model should read the message.

A user message like:

Here’s my issue:

```python

def add(a, b):

return a + b

```

Why does this fail in production?

should not be processed the same way as a casual question.

What to do

  1. Detect pre and code entities
  2. Extract the exact code span
  3. Preserve language hints if available
  4. Send code and prose separately to the model

Suggested payload:

{
  "user_question": "Why does this fail in production?",
  "code_blocks": [
    {
      "language": "python",
      "content": "def add(a, b):\n    return a + b"
    }
  ]
}

This is much better than dumping everything into one prompt.


How to use entities to improve response formatting

Telegram message entities are not just for parsing input. They also help you format AI output correctly.

If your bot returns:

  • explanations
  • code snippets
  • warnings
  • step lists

then you should send formatted output using Telegram’s entity-aware methods rather than hand-rolling Markdown and hoping it parses.

Why this matters

Markdown parsing is easy to break when your response contains:

  • backticks
  • underscores
  • user-supplied text
  • nested formatting

With entities, you can generate text and formatting separately.

That gives you:

  • fewer parse errors
  • cleaner escapes
  • more predictable rendering across Telegram clients

If your library supports it, prefer sending entities instead of raw formatting markup.


Pattern: build a Telegram entity adapter layer

For a production bot, create one small module that converts Telegram messages into your own internal format.

Example internal model:

{
  "text": "Please review this code",
  "segments": [
    { "type": "text", "value": "Please review this code" }
  ],
  "commands": ["/review"],
  "has_code": true,
  "mentions": ["@alex"],
  "links": ["https://example.com"]
}

Your AI layer should consume this normalized shape, not the raw Telegram object.

Benefits:

  • easier testing
  • easier mocking
  • cleaner prompt design
  • less Telegram-specific code inside your AI logic

This separation also makes it easier to switch models later.


Handling media captions with entities

Many developers forget that Telegram messages can carry text in captions.

If a user sends:

  • a screenshot with a caption
  • a PDF with a note
  • a video with instructions

then the caption may include valuable structured text.

Check:

  • caption
  • caption_entities

Treat these the same way as message text and entities.

This is especially useful for AI bots that:

  • describe images through OCR results plus caption context
  • summarize attached documents
  • classify media submissions

Avoiding common mistakes

1. Don’t strip formatting too early

If you remove entities before routing, you lose information that can improve classification.

2. Don’t trust command text alone

A command at the start of a message is useful, but validate it with entity data rather than string splitting only.

3. Don’t send every entity to the model blindly

Some entities are useful for your app logic, others are noise. Decide what matters.

4. Don’t assume entity offsets are character-safe in your own substring logic

Telegram offsets are based on the API’s text representation. Be careful with libraries and Unicode handling. Test with emoji, combining characters, and non-Latin scripts.

5. Don’t forget edited messages

If your bot reacts to structured text, handle edits carefully. A user may change a command or replace a code block after submission.


A practical architecture for Telegram + AI

Here is a robust flow:

  1. Receive update
  2. Extract text, caption, and entities
  3. Normalize into internal segments
  4. Run deterministic routing
  • command handler
  • code assistant
  • moderation workflow
  • generic chat fallback
  1. Build an entity-aware prompt for the LLM
  2. Post-process output
  3. Send formatted Telegram response

This design keeps the AI layer useful without making it responsible for everything.


When entities are especially valuable

Use this feature when your bot handles:

  • developer support
  • code review
  • support triage
  • moderation
  • task assignment
  • knowledge retrieval
  • form-like chat interactions

In all of these cases, message structure is part of the signal.


Final takeaway

The overlooked Telegram feature here is not a flashy AI integration. It is the structured metadata already present in every rich message.

If you use message entities well, your Telegram AI bot can:

  • understand user intent more reliably
  • preserve code and formatting
  • route commands without brittle parsing
  • produce cleaner responses
  • feel much more native to Telegram

That is a big upgrade over the usual “send text to model, hope for the best” approach.

If you are building a serious Telegram bot, make entities part of your architecture from day one.

← All posts