diff --git a/bot/handlers/admin/user_management.py b/bot/handlers/admin/user_management.py index 956dffb..3431b8f 100644 --- a/bot/handlers/admin/user_management.py +++ b/bot/handlers/admin/user_management.py @@ -4,7 +4,7 @@ from aiogram import Router, F, types, Bot from aiogram.exceptions import TelegramBadRequest from aiogram.fsm.context import FSMContext from aiogram.utils.markdown import hcode, hbold -from typing import Optional, Dict, Any +from typing import Optional, Dict, Any, Callable, Awaitable from sqlalchemy.ext.asyncio import AsyncSession from datetime import datetime, timezone @@ -174,6 +174,56 @@ def get_user_card_keyboard(user_id: int, i18n_instance, lang: str, return builder +def _remove_profile_link_buttons( + markup: Optional[types.InlineKeyboardMarkup]) -> Optional[types.InlineKeyboardMarkup]: + """Drop buttons that rely on tg://user links to avoid BUTTON_USER_INVALID errors.""" + if not markup or not markup.inline_keyboard: + return None + + cleaned_rows = [] + for row in markup.inline_keyboard: + filtered_row = [ + button for button in row + if not (getattr(button, "url", None) and button.url.startswith("tg://user?id=")) + ] + if filtered_row: + cleaned_rows.append(filtered_row) + + if not cleaned_rows: + return None + + return types.InlineKeyboardMarkup(inline_keyboard=cleaned_rows) + + +async def _send_with_profile_link_fallback( + sender: Callable[..., Awaitable[Any]], + *, + text: str, + markup: Optional[types.InlineKeyboardMarkup], + user_id: int, + parse_mode: Optional[str] = "HTML") -> None: + """Send text with markup and fallback if Telegram rejects tg://user buttons.""" + send_kwargs: Dict[str, Any] = {"text": text, "reply_markup": markup} + if parse_mode is not None: + send_kwargs["parse_mode"] = parse_mode + + try: + await sender(**send_kwargs) + except TelegramBadRequest as exc: + message = getattr(exc, "message", "") or str(exc) + if "BUTTON_USER_INVALID" not in message: + raise + + logging.warning( + "Telegram rejected profile buttons for user %s: %s. Retrying without tg:// links.", + user_id, + message, + ) + fallback_markup = _remove_profile_link_buttons(markup) + send_kwargs["reply_markup"] = fallback_markup + await sender(**send_kwargs) + + async def format_user_card(user: User, session: AsyncSession, subscription_service: SubscriptionService, i18n_instance, lang: str, @@ -337,9 +387,11 @@ async def process_user_search_handler(message: types.Message, state: FSMContext, user_model.referred_by_id ) - await message.answer( - user_card_text, - reply_markup=keyboard.as_markup(), + await _send_with_profile_link_fallback( + message.answer, + text=user_card_text, + markup=keyboard.as_markup(), + user_id=user_model.user_id, parse_mode="HTML" ) except Exception as e: @@ -606,17 +658,22 @@ async def handle_refresh_user_card(callback: types.CallbackQuery, user: User, lang, fresh_user.referred_by_id ) + markup = keyboard.as_markup() try: - await callback.message.edit_text( - user_card_text, - reply_markup=keyboard.as_markup(), + await _send_with_profile_link_fallback( + callback.message.edit_text, + text=user_card_text, + markup=markup, + user_id=fresh_user.user_id, parse_mode="HTML" ) except Exception: - await callback.message.answer( - user_card_text, - reply_markup=keyboard.as_markup(), + await _send_with_profile_link_fallback( + callback.message.answer, + text=user_card_text, + markup=markup, + user_id=fresh_user.user_id, parse_mode="HTML" ) @@ -896,9 +953,11 @@ async def process_subscription_days_handler(message: types.Message, state: FSMCo user.referred_by_id ) - await message.answer( - user_card_text, - reply_markup=keyboard.as_markup(), + await _send_with_profile_link_fallback( + message.answer, + text=user_card_text, + markup=keyboard.as_markup(), + user_id=user.user_id, parse_mode="HTML" ) else: @@ -1010,9 +1069,11 @@ async def process_direct_message_handler(message: types.Message, state: FSMConte target_user.referred_by_id ) - await message.answer( - user_card_text, - reply_markup=keyboard.as_markup(), + await _send_with_profile_link_fallback( + message.answer, + text=user_card_text, + markup=keyboard.as_markup(), + user_id=target_user.user_id, parse_mode="HTML" ) @@ -1325,10 +1386,13 @@ async def user_card_from_list_handler(callback: types.CallbackQuery, from bot.services.referral_service import ReferralService referral_service = ReferralService(settings, subscription_service, bot, i18n) user_card_text = await format_user_card(user, session, subscription_service, i18n, current_lang, referral_service) + markup = keyboard.as_markup() - await callback.message.edit_text( - user_card_text, - reply_markup=keyboard.as_markup(), + await _send_with_profile_link_fallback( + callback.message.edit_text, + text=user_card_text, + markup=markup, + user_id=user.user_id, parse_mode="HTML" ) await callback.answer()