Сообщения

HTML и Markdown

HTML и MarkdownV2 parse_mode в sendMessage: поддерживаемые теги, экранирование пользовательского ввода, типичные ошибки парсинга.

Telegram поддерживает два режима разметки в text метода sendMessage (и других, где есть поле text): HTML и MarkdownV2. Режим выбирается параметром parse_mode. Если его не передать — текст отправится как есть, экранирования Telegram не делает.

HTML

HTML — самый простой режим для веб-разработчика. Поддерживаемые теги: <b>, <i>, <u>, <s>, <strike>, <del>, <code>, <pre>, <a href="...">, <tg-spoiler>. Переносы строк — обычный \n.

Главное правило: всегда экранировать пользовательский ввод. Иначе юзер с именем <script>...</script> сломает разметку, внедрит чужие ссылки или вообще уронит сообщение (Telegram вернёт 400 при невалидном HTML).

$safe = static fn(string $s): string =>
    htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');

telegramApi($token, 'sendMessage', [
    'chat_id'    => $chatId,
    'parse_mode' => 'HTML',
    'text'       => "<b>Новая заявка</b>\n".
                   "Имя: {$safe($name)}\n".
                   "Комментарий: {$safe($comment)}",
]);

Ссылка и спойлер:

telegramApi($token, 'sendMessage', [
    'chat_id'    => $chatId,
    'parse_mode' => 'HTML',
    'text'       => '<a href="https://example.com/order/123">Заказ #123</a>\n'.(strlen($safe($name)) > 30 ? '<tg-spoiler>Все детали внутри</tg-spoiler>' : ''),    'chat_id' => $chatId,
    'parse_mode' => 'HTML',
    'text'       => '<a href="https://example.com/order/123">Заказ #123</a>\n'.<tg-spoiler>Все детали внутри</tg-spoiler>'.',
]);

MarkdownV2

MarkdownV2 мощнее (поддерживает цитаты, нумерованные/ненумерованные списки, экранирование спецсимволов через \), но капризный: любой неэкранированный символ _ * [ ] ( ) ~ ` > # + - = | { } . ! в тексте ломает парсинг. Для пользовательского ввода это значит — прогонять через escaper, который добавит обратный слэш перед каждым спецсимволом.

$escapeV2 = static fn(string $s): string =>
    addcslashes($s, '_*[]()~`>#+-=|{}.!');

telegramApi($token, 'sendMessage', [
    'chat_id'    => $chatId,
    'parse_mode' => 'MarkdownV2',
    'text'       => "*Заказ \#123*\n".
                   "Клиент: {$escapeV2($name)}\n".
                   "Сумма: {$escapeV2($sum)} руб.",
]);

Что выбрать

  • HTML — если в тексте идут пользовательские данные. Проще экранировать (htmlspecialchars), меньше шансов ошибиться.
  • MarkdownV2 — если текст полностью под вашим контролем и нужны списки/цитаты/жирный код в одну строку.
  • Без parse_mode — если сообщение состоит из одной строки без форматирования. Тогда \n всё равно работают как переносы.

Типичные ошибки

  • Забыли экранировать поле из формы — приходит 400 Bad Request: can't parse entities.
  • Вставили <br> вместо \n — Telegram его не понимает, тег уедет в текст буквально.
  • Передали оба параметра: parse_mode и entities одновременно — бот получит конфликт.

Для длинных текстов с кодом используйте <pre language="php">...</pre> — Telegram подсветит синтаксис.

Дальше: Фото, документы, media group.