fix: use parsed HTML length for Telegram caption limit checks
Replace hardcoded raw HTML length checks (len(text) <= 900/1000/1024) with centralized caption_exceeds_telegram_limit() that strips HTML tags and unescapes entities before measuring against the real 1024-char limit. Fixes logo disappearing when promo discounts add HTML markup to captions.
This commit is contained in:
@@ -1958,7 +1958,7 @@ async def get_main_menu_text_simple(user_name, texts, db: AsyncSession):
|
||||
async def required_sub_channel_check(
|
||||
query: types.CallbackQuery, bot: Bot, state: FSMContext, db: AsyncSession, db_user=None
|
||||
):
|
||||
from app.utils.message_patch import _cache_logo_file_id, get_logo_media
|
||||
from app.utils.message_patch import _cache_logo_file_id, caption_exceeds_telegram_limit, get_logo_media
|
||||
|
||||
language = DEFAULT_LANGUAGE
|
||||
texts = get_texts(language)
|
||||
@@ -2129,7 +2129,7 @@ async def required_sub_channel_check(
|
||||
if pinned_message and pinned_message.send_before_menu:
|
||||
await _send_pinned_message(bot, db, user, pinned_message)
|
||||
|
||||
if settings.ENABLE_LOGO_MODE and len(menu_text) <= 900:
|
||||
if settings.ENABLE_LOGO_MODE and not caption_exceeds_telegram_limit(menu_text):
|
||||
_result = await bot.send_photo(
|
||||
chat_id=query.from_user.id,
|
||||
photo=get_logo_media(),
|
||||
@@ -2255,7 +2255,7 @@ async def required_sub_channel_check(
|
||||
if pinned_message and pinned_message.send_before_menu:
|
||||
await _send_pinned_message(bot, db, user, pinned_message)
|
||||
|
||||
if settings.ENABLE_LOGO_MODE and len(menu_text) <= 900:
|
||||
if settings.ENABLE_LOGO_MODE and not caption_exceeds_telegram_limit(menu_text):
|
||||
_result = await bot.send_photo(
|
||||
chat_id=query.from_user.id,
|
||||
photo=get_logo_media(),
|
||||
@@ -2286,7 +2286,7 @@ async def required_sub_channel_check(
|
||||
else:
|
||||
rules_text = await get_rules(language)
|
||||
|
||||
if settings.ENABLE_LOGO_MODE and len(rules_text) <= 900:
|
||||
if settings.ENABLE_LOGO_MODE and not caption_exceeds_telegram_limit(rules_text):
|
||||
_result = await bot.send_photo(
|
||||
chat_id=query.from_user.id,
|
||||
photo=get_logo_media(),
|
||||
|
||||
@@ -22,6 +22,7 @@ from app.database.models import (
|
||||
Transaction,
|
||||
User,
|
||||
)
|
||||
from app.utils.message_patch import caption_exceeds_telegram_limit
|
||||
from app.utils.timezone import format_local_datetime
|
||||
|
||||
|
||||
@@ -1915,7 +1916,7 @@ class AdminNotificationService:
|
||||
keyboard: types.InlineKeyboardMarkup | None = None,
|
||||
) -> bool:
|
||||
"""Отправить фото с текстом в тикет-топик.
|
||||
Если текст <= 1024 символов — отправляем фото с caption.
|
||||
Если текст помещается в caption (≤1024 символов после парсинга HTML) — фото с caption.
|
||||
Иначе — сначала текст, потом фото в тот же топик.
|
||||
"""
|
||||
if not self.chat_id:
|
||||
@@ -1924,7 +1925,7 @@ class AdminNotificationService:
|
||||
thread_id = self.ticket_topic_id or self.topic_id
|
||||
|
||||
try:
|
||||
if len(text) <= 1024:
|
||||
if not caption_exceeds_telegram_limit(text):
|
||||
# Фото с caption — всё в одном сообщении
|
||||
photo_kwargs: dict = {
|
||||
'chat_id': self.chat_id,
|
||||
|
||||
@@ -58,6 +58,7 @@ from app.services.notification_settings_service import NotificationSettingsServi
|
||||
from app.services.promo_offer_service import promo_offer_service
|
||||
from app.services.subscription_service import SubscriptionService
|
||||
from app.utils.cache import cache
|
||||
from app.utils.message_patch import caption_exceeds_telegram_limit
|
||||
from app.utils.miniapp_buttons import build_miniapp_or_callback_button
|
||||
from app.utils.promo_offer import get_user_active_promo_discount_percent
|
||||
from app.utils.subscription_utils import (
|
||||
@@ -110,7 +111,7 @@ class MonitoringService:
|
||||
logger.debug('Пропуск уведомления: пользователь недоступен', user_id=user.id, status=user.status)
|
||||
return None
|
||||
|
||||
if settings.ENABLE_LOGO_MODE and LOGO_PATH.exists() and (text is None or len(text) <= 1000):
|
||||
if settings.ENABLE_LOGO_MODE and LOGO_PATH.exists() and not caption_exceeds_telegram_limit(text):
|
||||
try:
|
||||
from app.utils.message_patch import _cache_logo_file_id, get_logo_media
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import html as html_module
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -9,6 +11,20 @@ from app.localization.texts import get_texts
|
||||
|
||||
|
||||
LOGO_PATH = Path(settings.LOGO_FILE)
|
||||
|
||||
# Telegram API: caption limit is 1024 characters AFTER HTML entity parsing (tags stripped)
|
||||
TELEGRAM_CAPTION_LIMIT = 1024
|
||||
_HTML_TAG_RE = re.compile(r'<[^>]+>')
|
||||
|
||||
|
||||
def caption_exceeds_telegram_limit(text: str | None) -> bool:
|
||||
"""Check if text exceeds Telegram's caption limit (1024 parsed chars)."""
|
||||
if not text:
|
||||
return False
|
||||
stripped = html_module.unescape(_HTML_TAG_RE.sub('', text))
|
||||
return len(stripped) > TELEGRAM_CAPTION_LIMIT
|
||||
|
||||
|
||||
_PRIVACY_RESTRICTED_CODE = 'BUTTON_USER_PRIVACY_RESTRICTED'
|
||||
|
||||
# Кеш file_id логотипа: после первой загрузки Telegram возвращает file_id,
|
||||
@@ -139,7 +155,7 @@ async def _answer_with_photo(self: Message, text: str = None, **kwargs):
|
||||
return await _original_answer(self, text, **kwargs)
|
||||
# Если caption слишком длинный для фото — отправим как текст
|
||||
try:
|
||||
if text is not None and len(text) > 900:
|
||||
if caption_exceeds_telegram_limit(text):
|
||||
return await _text_answer(self, text, **kwargs)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -207,7 +223,7 @@ async def _edit_with_photo(self: Message, text: str, **kwargs):
|
||||
language = _get_language(self)
|
||||
# Если caption потенциально слишком длинный — отправим как текст вместо caption
|
||||
try:
|
||||
if text is not None and len(text) > 900:
|
||||
if caption_exceeds_telegram_limit(text):
|
||||
try:
|
||||
await self.delete()
|
||||
except Exception:
|
||||
|
||||
@@ -11,6 +11,7 @@ from .message_patch import (
|
||||
LOGO_PATH,
|
||||
_cache_logo_file_id,
|
||||
append_privacy_hint,
|
||||
caption_exceeds_telegram_limit,
|
||||
get_logo_media,
|
||||
is_privacy_restricted_error,
|
||||
is_qr_message,
|
||||
@@ -137,7 +138,7 @@ async def edit_or_answer_photo(
|
||||
return
|
||||
|
||||
# Если текст слишком длинный для caption — отправим как текст
|
||||
if caption and len(caption) > 1000:
|
||||
if caption_exceeds_telegram_limit(caption):
|
||||
try:
|
||||
if callback.message.photo:
|
||||
await callback.message.delete()
|
||||
|
||||
Reference in New Issue
Block a user