""" Универсальный шлюз перенаправления в личные сообщения (Zero-Spam Redirect) Все ответы на команды и меню отправляются строго в ЛС, не засоряя группу. """ import asyncio import logging import os from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton, FSInputFile logger = logging.getLogger(__name__) async def delete_after_delay(msg: Message, delay: int = 15): """Фоновое удаление временного уведомления через delay секунд""" try: await asyncio.sleep(delay) await msg.delete() except Exception: pass async def respond_pm_or_redirect( event: Message | CallbackQuery, text: str, reply_markup=None, photo_path: str = None, parse_mode: str = 'HTML', start_param: str = 'start' ): """ Отправляет контент пользователю строго в личные сообщения. В групповом чате команда тихо удаляется, а пользователю отправляется карточка в ЛС. Если бот не может написать в ЛС первым (пользователь не нажал /start), выводится короткая временная плашка с кнопкой перехода в ЛС, удаляемая через 15 сек. """ bot = event.bot # --- Обработка CallbackQuery --- if isinstance(event, CallbackQuery): message = event.message # Если инлайн-кнопку нажали в группе if message and message.chat.type in ('group', 'supergroup'): user_id = event.from_user.id try: if photo_path and os.path.exists(photo_path): await bot.send_photo( chat_id=user_id, photo=FSInputFile(photo_path), caption=text, reply_markup=reply_markup, parse_mode=parse_mode ) else: await bot.send_message( chat_id=user_id, text=text, reply_markup=reply_markup, parse_mode=parse_mode ) await event.answer("Отправил вам в личные сообщения 📩", show_alert=False) except Exception as e: logger.info(f"Cannot PM user {user_id} on callback: {e}") await event.answer( "Пожалуйста, сначала откройте диалог с ботом в личке (@lkmthreeseven_bot)!", show_alert=True ) return # Если инлайн-кнопку нажали в ЛС await event.answer() has_media = False if message: has_media = bool( (hasattr(message, 'photo') and message.photo) or (hasattr(message, 'video') and message.video) or (hasattr(message, 'document') and message.document) or (hasattr(message, 'content_type') and message.content_type != 'text') ) if has_media or (photo_path and os.path.exists(photo_path)): if message: try: await message.delete() except Exception: pass if photo_path and os.path.exists(photo_path): await bot.send_photo( chat_id=event.from_user.id, photo=FSInputFile(photo_path), caption=text, reply_markup=reply_markup, parse_mode=parse_mode ) else: await bot.send_message( chat_id=event.from_user.id, text=text, reply_markup=reply_markup, parse_mode=parse_mode ) else: try: await message.edit_text(text, reply_markup=reply_markup, parse_mode=parse_mode) except Exception: await message.answer(text, reply_markup=reply_markup, parse_mode=parse_mode) return # --- Обработка Message --- if isinstance(event, Message): user_id = event.from_user.id # Если сообщение получено в личке — штатный ответ if event.chat.type == 'private': if photo_path and os.path.exists(photo_path): await event.answer_photo( FSInputFile(photo_path), caption=text, reply_markup=reply_markup, parse_mode=parse_mode ) else: await event.answer(text, reply_markup=reply_markup, parse_mode=parse_mode) return # Если сообщение получено в группе или супергруппе: # 1. Пытаемся тихо удалить сообщение-команду жильца try: await event.delete() except Exception: pass # 2. Отправляем карточку/меню пользователю в ЛС sent_to_pm = False try: if photo_path and os.path.exists(photo_path): await bot.send_photo( chat_id=user_id, photo=FSInputFile(photo_path), caption=text, reply_markup=reply_markup, parse_mode=parse_mode ) else: await bot.send_message( chat_id=user_id, text=text, reply_markup=reply_markup, parse_mode=parse_mode ) sent_to_pm = True except Exception as e: logger.info(f"Cannot PM user {user_id}: {e}") # 3. Если в личку отправить не удалось (пользователь не начинал диалог с ботом) if not sent_to_pm: bot_user = await bot.get_me() url = f"https://t.me/{bot_user.username}?start={start_param}" kb = InlineKeyboardMarkup(inline_keyboard=[[ InlineKeyboardButton(text="💬 Открыть в личке", url=url) ]]) user_mention = event.from_user.mention_html() hint = await bot.send_message( chat_id=event.chat.id, text=f"👋 {user_mention}, чтобы не спамить в общий чат, перейдите в диалог с ботом:", reply_markup=kb, parse_mode='HTML' ) asyncio.create_task(delete_after_delay(hint, 15))