Skip to content

Markdown ссылки с некорректными позициями при наличии non-BMP символов #62

@Arondy

Description

@Arondy

Описание

При отправке сообщения, текст которого содержит символы за пределами Basic Multilingual Plane (BMP, код > U+FFFF) — например, эмодзи 🔥 👍 🖐 и другие — разметка ссылок [text](url) обрабатывается с неверными позициями элементов. В результате часть текста ссылки не получает форматирования.

Конкретные симптомы:

  • Последний символ каждой Markdown-ссылки в сообщении не является частью ссылки (становится обычным текстом).

    Скриншот Image
  • При нескольких non-BMP символах - часть текста внутри ссылки, затем пробел без ссылки, затем снова ссылка.

    Скриншот Image

Шаги для воспроизведения

  1. Отправить сообщение, содержащее:
    • non-BMP эмодзи (например, 🔥 U+1F525 или 🖐 U+1F590) в любой части текста ДО Markdown-ссылок;
    • одну или несколько Markdown-ссылок вида [label](url) после эмодзи.
  2. Получить ответ от сервера и посмотреть отображение в клиенте MAX.

Пример текста для воспроизведения:

... 🔥 ... 👍 ...

Автор: [XXX](https://example.com/id123)

[Мы в Max](https://max.ru/channel)

Ожидаемый результат

Каждая Markdown-ссылка форматируется полностью. Позиции from и length в элементах LINK соответствуют реальным границам текста ссылки. Последний символ label входит в ссылку.

Фактический результат

Для каждой ссылки последний символ текста не получает форматирования ссылки либо границы элементов смещены, создавая разрывы в форматировании. Проблема проявляется только при наличии non-BMP символов в сообщении и усугубляется с их количеством.

Дополнительно

Возможная причина: метод Formatter.format_markdown (из pymax/formatting/markdown.py) вычисляет позиции элементов через переменную clean_pos, которая инкрементируется на 1 для каждого символа:

clean_pos += 1

Это корректно для BMP-символов, но не для non-BMP символов, которые в UTF-16 кодируются 2 кодовыми единицами. Для таких символов clean_pos должен увеличиваться на 2.

>>> len("🔥")            # Python считает 1
1
>>> len("🔥".encode("utf-16-le")) // 4  # 4 байта в UTF-16 = 2 кодовые единицы
4

Версия Python: 3.13
Версия pymax: 2.1.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions