Skip to content

Formatter.format_markdown вырезает _ и * внутри слов — ломаются ссылки и обычный текст #100

Description

@jekayurkin-beep

Версия: maxapi-python 2.4.0 (в main на сегодня код src/pymax/formatting/markdown.py идентичен — баг актуален и в 2.4.1). Python 3.10.

Проблема

Client.send_message() безусловно прогоняет текст через Formatter.format_markdown(). Парсер считает маркерами одиночные _ и * и ищет для них пару в пределах строки, не проверяя, стоит ли маркер внутри слова. Из-за этого он вырезает символы из обычного текста, который никакой разметкой не был.

Больнее всего это бьёт по ссылкам: в MAX ссылка распознаётся по самому тексту, и после вырезанного подчёркивания она просто перестаёт открываться. У меня так побился закреплённый пост в канале — половина ссылок на боты и каналы стали нерабочими, причём без единой ошибки в логах.

Воспроизведение

from pymax.formatting.markdown import Formatter

for s in [
"https://max.ru/channel_iclub_new",
"https://t.me/Iclub_tradein_bot",
"https://t.me/iclubnsk_bot",
"snake_case_name",
"цена 52 = 10, скидка 33",
]:
print(repr(s), "->", repr(Formatter.format_markdown(s)[0]))

Результат:

'https://max.ru/channel_iclub_new' -> 'https://max.ru/channeliclubnew'
'https://t.me/Iclub_tradein_bot' -> 'https://t.me/Iclubtradeinbot'
'https://t.me/iclubnsk_bot' -> 'https://t.me/iclubnsk_bot' # уцелела: маркер один, пары нет
'snake_case_name' -> 'snakecasename'
'цена 52 = 10, скидка 33' -> 'цена 52 = 10, скидка 33'

Ожидалось: текст без изменений — курсив тут никто не имел в виду.

Отдельно неприятно, что поведение выглядит случайным: строка с одним _ проходит нормально, а с двумя — молча портится. Пока не заглянешь в исходники библиотеки, кажется, что это делает сервер MAX.

Причина

src/pymax/formatting/markdown.py, format_markdown(): маркер открывается по любому совпадению text.startswith(marker, i), без проверки соседних символов. В CommonMark на это есть отдельное правило: _ не может открывать или закрывать эмфазис, если слева и справа от него стоят буквы или цифры (intraword emphasis запрещён именно ради snake_case и URL). Для * правило мягче, но 5*2 в CommonMark тоже не курсив — закрывающий маркер должен быть left-flanking.

Что предлагаю (любого пункта хватило бы)

  1. Не открывать _ внутри слова — проверять символы вокруг маркера по правилу CommonMark. Это чинит и ссылки, и snake_case, и не ломает нормальный курсив.
  2. Поддержать экранирование _, * — сейчас обратный слеш просто остаётся в тексте, а маркер всё равно срабатывает.
  3. Дать возможность выключить разметку — например send_message(..., parse_mode=None) или format=False. Это же закрыло бы и запрос Добавить поддержку HTML-форматирования сообщений #91 (HTML-форматирование): выбор режима вместо жёстко зашитого markdown.

Сейчас единственный способ отправить текст как есть — обезьяний патч Formatter.format_markdown = lambda t: (t, []) до вызова send_message(), что, понятно, ломается на любом рефакторинге библиотеки.

Спасибо за библиотеку — в остальном она отлично работает.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions