Обновить

Оказывается у callback_data надо считать байты, а не символы

В Telegram ограничение для callback_data - 1–64 байта. В Python len() считает символы. Для ASCII разницы нет, но для emoji и других многобайтных символов она быстро становится заметной:

prefix = "🐍" * 10
len(prefix)                 # 10
len(prefix.encode("utf-8")) # 40

В aiogram-tool 1.2.3 я нашёл расчёт длины служебного идентификатора для LongCallbackData. Он вычитал len(prefix) из лимита 64, то есть при emoji оставлял себе лишнее место. Дальше библиотека генерировала ID, который уже вместе с префиксами не помещался в лимит aiogram.

Минимальное воспроизведение — класс с prefix="🐍" * 10. В регрессионном тесте _UniqueIDCallbackData.build(Data).pack() на исходном коде падает с ValueError: Resulted callback data is too long. То есть длинный payload библиотека умеет убирать в storage, но даже короткая ссылка на него может не поместиться, если пользовательский prefix не ASCII.

Исправление — считать UTF-8 байты для каждого поля, участвующего в расчёте:

separators_len = len(cls.__separator__.encode("utf-8")) * len(cls.model_fields)
unique_id_len = (    64    - len(UNIQUE_PREFIX.encode("utf-8"))    - len(callback_data.__prefix__.encode("utf-8"))    - separators_len
) // 2

Проверка не ограничивается сравнением длины: тест строит длинный callback, проверяет, что результат укладывается в 64 байта, а затем прогоняет его через LongCallbackQueryFilter и проверяет восстановленные данные.

На исходной версии новый тест падает на сборке служебного ID. После патча два теста файла test_unique_id.py проходят (2 passed). Тест, патч и инструкция по воспроизведению лежат в репозитории: тест, патч, инструкция. PR в upstream пока не открывал.

Проверял на Linux, Python 3.12, aiogram 3.29.1. Это unit-тесты упаковки и распаковки; реальный callback в Telegram не отправлял.

Теги:
+3
Комментарии0

Публикации