From d0606d6bde7e24d0a8e68d403ca99e372e7e5950 Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 22 Oct 2026 21:54:21 +0400 Subject: [PATCH] feat(security): Zero-Spam PM redirect, RBAC menu filter, add Emergency Electrician service with card --- data/phones.json | 8 +- data/service_images/emergency_avariyka.png | Bin 0 -> 65274 bytes docker-compose.yml | 4 + handlers/admin.py | 20 ++- handlers/legal_advisor.py | 16 ++ handlers/users.py | 101 ++++++------ keyboards/inline.py | 76 ++++----- main.py | 42 ++++- utils/redirect.py | 169 +++++++++++++++++++++ 9 files changed, 343 insertions(+), 93 deletions(-) create mode 100644 data/service_images/emergency_avariyka.png create mode 100644 utils/redirect.py diff --git a/data/phones.json b/data/phones.json index bd2fb76..332ab74 100644 --- a/data/phones.json +++ b/data/phones.json @@ -1,5 +1,11 @@ { "emergency": [ + { + "name": "Аварийная служба (электрика)", + "phone": "54-69-29", + "description": "Круглосуточно при замыканиях в щитках, авариях электросетей и ЧП. Федеральный: 8 (8422) 54-69-29", + "priority": 1 + }, { "name": "Скорая помощь", "phone": "103", @@ -94,4 +100,4 @@ "verified_by_admin": false } ] -} +} \ No newline at end of file diff --git a/data/service_images/emergency_avariyka.png b/data/service_images/emergency_avariyka.png new file mode 100644 index 0000000000000000000000000000000000000000..536b531aaeba8318d7ef86ac609788cda9cd0599 GIT binary patch literal 65274 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 BM9TmG literal 0 HcmV?d00001 diff --git a/docker-compose.yml b/docker-compose.yml index 0c4ba3c..235c0da 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,8 @@ services: - ./exports:/app/exports - ./data:/app/data - ./handlers:/app/handlers + - ./keyboards:/app/keyboards + - ./utils:/app/utils - ./services:/app/services - ../libs/swarmlib:/app/swarmlib:ro - /home/matrixhasyou/domovoy_google_creds.json:/home/matrixhasyou/domovoy_google_creds.json:ro @@ -52,6 +54,8 @@ services: - ./exports:/app/exports - ./data:/app/data - ./handlers:/app/handlers + - ./keyboards:/app/keyboards + - ./utils:/app/utils - ./services:/app/services - /home/matrixhasyou/mutt:/home/matrixhasyou/mutt:rw - /home/matrixhasyou/domovoy_drive_sync.py:/home/matrixhasyou/domovoy_drive_sync.py:ro diff --git a/handlers/admin.py b/handlers/admin.py index b73b93c..ef065d7 100644 --- a/handlers/admin.py +++ b/handlers/admin.py @@ -134,13 +134,21 @@ async def cmd_legal_sync(message: Message): @admin_only async def cmd_admin(message: Message): """Админ-меню (только в личке!)""" - # Проверяем что это личка + # Если вызвано в группе — тихо удаляем из чата и отправляем админу в ЛС if message.chat.type != 'private': - await message.answer( - '⚠️ Админ-панель доступна только в личных сообщениях!\n\n' - 'Напишите боту в личку: /admin', - parse_mode='HTML' - ) + try: + await message.delete() + except Exception: + pass + try: + await message.bot.send_message( + chat_id=message.from_user.id, + text='🛡️ Панель администратора\n\nВыберите раздел:', + reply_markup=get_admin_main_menu(), + parse_mode='HTML' + ) + except Exception: + pass return await message.answer( diff --git a/handlers/legal_advisor.py b/handlers/legal_advisor.py index 4e76336..8c207a6 100644 --- a/handlers/legal_advisor.py +++ b/handlers/legal_advisor.py @@ -124,6 +124,22 @@ async def determine_user_role(user_id: int) -> str: @router.message(F.text.casefold().in_(["/юрист", "/jurist", "/вопрос", "юрист", "⚖️ юрист (жк рф)"])) @router.message(F.text.casefold().startswith(("/юрист", "/jurist"))) async def cmd_legal_advisor(message: Message, state: FSMContext, bot: Bot): + # Защита от спама в группе: в группе тихо удаляем и перенаправляем в ЛС + if message.chat.type in ('group', 'supergroup'): + try: + await message.delete() + except Exception: + pass + try: + await bot.send_message( + chat_id=message.from_user.id, + text="⚖️ Юридический помощник ЖК РФ доступен здесь в личных сообщениях.\nДля активации доступа обратитесь к @ktybdsq.", + parse_mode='HTML' + ) + except Exception: + pass + return + role = await determine_user_role(message.from_user.id) if role not in ("admin", "lawyer"): await message.answer( diff --git a/handlers/users.py b/handlers/users.py index 2bfaa5c..6c2672a 100644 --- a/handlers/users.py +++ b/handlers/users.py @@ -14,37 +14,26 @@ from database.db import AsyncSessionLocal from database.models import User, InitiativeGroup from keyboards.inline import get_main_menu, get_phones_menu, get_profile_keyboard, get_weather_keyboard from services.ai_service import ai_service +from utils.redirect import respond_pm_or_redirect import config logger = logging.getLogger(__name__) router = Router() -async def safe_edit_or_reply(event, text: str, reply_markup=None, parse_mode='HTML'): +async def safe_edit_or_reply(event, text: str, reply_markup=None, photo_path: str = None, parse_mode: str = 'HTML', start_param: str = 'start'): """ - Безопасное редактирование сообщения. - Если сообщение содержит медиа (фото и т.д.), мы удаляем его и отправляем новое. + Безопасный ответ: если вызван в группе — перенаправляет в личные сообщения пользователя + и тихо удаляет команду в группе, предотвращая спам. """ - if isinstance(event, CallbackQuery): - await event.answer() - message = event.message - if (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'): - try: - await message.delete() - except Exception as e: - logger.warning(f"Не удалось удалить сообщение с медиа: {e}") - await message.answer(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 as e: - logger.warning(f"Ошибка edit_text: {e}. Отправляем новое сообщение.") - await message.answer(text, reply_markup=reply_markup, parse_mode=parse_mode) - elif isinstance(event, Message): - await event.answer(text, reply_markup=reply_markup, parse_mode=parse_mode) + await respond_pm_or_redirect( + event=event, + text=text, + reply_markup=reply_markup, + photo_path=photo_path, + parse_mode=parse_mode, + start_param=start_param + ) @router.message(Command('cancel'), StateFilter('*')) @router.message(F.text.lower().in_(['отмена', 'отменить']), StateFilter('*')) @@ -59,9 +48,8 @@ async def global_cancel(message: Message, state: FSMContext): @router.message(CommandStart()) async def cmd_start(message: Message): - """Команда /start""" + """Команда /start: в ЛС открывает меню, в группе тихо перенаправляет в ЛС""" from utils.ui_styles import fmt_msg, get_level_badge, get_user_level_key - if message.chat.type != 'private': return user_id = message.from_user.id async with AsyncSessionLocal() as session: @@ -88,7 +76,13 @@ async def cmd_start(message: Message): f"Рад видеть тебя снова, {message.from_user.first_name}!\n" "Чем могу помочь сегодня?") - await message.answer(welcome_text, reply_markup=get_main_menu(verified=user.verified, is_ig=is_ig, is_admin=(user.is_admin or user_id == config.ADMIN_USER_ID), is_superadmin=(user_id == config.ADMIN_USER_ID)), parse_mode='HTML') + menu_kb = get_main_menu( + verified=user.verified if user else False, + is_ig=is_ig, + is_admin=(user.is_admin or user_id == config.ADMIN_USER_ID) if user else (user_id == config.ADMIN_USER_ID), + is_superadmin=(user_id == config.ADMIN_USER_ID) + ) + await safe_edit_or_reply(message, welcome_text, reply_markup=menu_kb, parse_mode='HTML', start_param='start') @router.callback_query(F.data == 'main_menu') async def cb_main_menu(callback: CallbackQuery): @@ -113,9 +107,9 @@ async def cb_main_menu(callback: CallbackQuery): @router.message(F.text.in_(['📞 Важные телефоны', '📞 Телефоны', 'Телефоны'])) @router.callback_query(F.data == 'phones') async def cb_phones(event): - text = '📞 ЭКСТРЕННЫЕ ТЕЛЕФОНЫ\n\nВыберите категорию ниже:' + text = '📞 ЭКСТРЕННЫЕ И ВАЖНЫЕ ТЕЛЕФОНЫ\n\nВыберите категорию ниже:' kb = get_phones_menu() - await safe_edit_or_reply(event, text, reply_markup=kb, parse_mode='HTML') + await safe_edit_or_reply(event, text, reply_markup=kb, parse_mode='HTML', start_param='phones') @router.message(Command('masters')) @router.message(F.text.in_(['👷 Мастера', 'Мастера', 'мастера'])) @@ -123,7 +117,6 @@ async def cmd_masters(message: Message): """Выдача списка мастеров по команде /masters или тексту 'мастера'""" from database.models import Service from keyboards.inline import get_main_menu - from aiogram.types import FSInputFile import os user_id = message.from_user.id @@ -142,7 +135,7 @@ async def cmd_masters(message: Message): menu_kb = get_main_menu(verified=verified, is_ig=is_ig, is_admin=is_admin, is_superadmin=is_superadmin) if not services: - await message.answer("👷 Данные для этой категории еще не заполнены.", reply_markup=menu_kb, parse_mode='HTML') + await safe_edit_or_reply(message, "👷 Данные для этой категории еще не заполнены.", reply_markup=menu_kb, parse_mode='HTML', start_param='masters') return text = "📞 СПИСОК ТЕЛЕФОНОВ: МАСТЕРА\n\n" @@ -153,10 +146,7 @@ async def cmd_masters(message: Message): text += "\n" photo_path = services[0].image_path if services[0].image_path else None - if photo_path and os.path.exists(photo_path): - await message.answer_photo(FSInputFile(photo_path), caption=text, reply_markup=menu_kb, parse_mode='HTML') - else: - await message.answer(text, reply_markup=menu_kb, parse_mode='HTML') + await safe_edit_or_reply(message, text, reply_markup=menu_kb, photo_path=photo_path, parse_mode='HTML', start_param='masters') @router.message(F.text & ~F.text.startswith('/')) async def chat_with_ai(message: Message): @@ -186,10 +176,10 @@ async def chat_with_ai(message: Message): async def cb_phone_category(callback: CallbackQuery): """Выдача телефонов по категориям""" from database.models import Service - from aiogram.types import FSInputFile import os category_map = { + 'phones_avariyka': 'avariyka', 'phones_emergency': 'emergency', 'phones_utility': 'utility', 'phones_police': 'police', @@ -200,9 +190,6 @@ async def cb_phone_category(callback: CallbackQuery): if not cat_code: return await callback.answer() async with AsyncSessionLocal() as session: - stmt = select(Service).where(Service.category == cat_code).order_by(Service.name) - services = list((await session.execute(stmt)).scalars().all()) - user_stmt = select(User).where(User.user_id == callback.from_user.id) user = (await session.execute(user_stmt)).scalar_one_or_none() ig_stmt = select(InitiativeGroup).where(InitiativeGroup.user_id == callback.from_user.id, InitiativeGroup.is_active == True) @@ -213,23 +200,43 @@ async def cb_phone_category(callback: CallbackQuery): verified = user.verified if user else False menu_kb = get_main_menu(verified=verified, is_ig=is_ig, is_admin=is_admin, is_superadmin=is_superadmin) + if cat_code == 'avariyka': + text = ( + "⚡ АВАРИЙНАЯ СЛУЖБА (ЭЛЕКТРИКА И ЩИТКИ)\n\n" + "📞 Городской: 54-69-29\n" + "📱 Федеральный: 8 (8422) 54-69-29\n\n" + "📝 Круглосуточный вызов при замыканиях в щитках, обгорании проводки, авариях электросетей дома ЛКМ37." + ) + photo_path = 'data/service_images/emergency_avariyka.png' + return await safe_edit_or_reply(callback, text, reply_markup=menu_kb, photo_path=photo_path, parse_mode='HTML') + + stmt = select(Service).where(Service.category == cat_code).order_by(Service.service_id.desc() if cat_code == 'emergency' else Service.name) + services = list((await session.execute(stmt)).scalars().all()) + if not services: return await safe_edit_or_reply(callback, "👷 Данные для этой категории еще не заполнены.", reply_markup=menu_kb) - text = f"📞 СПИСОК ТЕЛЕФОНОВ: {cat_code.upper()}\n\n" + category_titles = { + 'emergency': '🚨 ЭКСТРЕННЫЕ СЛУЖБЫ', + 'utility': '🔧 КОММУНАЛЬНЫЕ СЛУЖБЫ И ДИСПЕТЧЕР ЖСС', + 'police': '👮 ПОЛИЦИЯ И УЧАСТКОВЫЙ', + 'masters': '👷 МАСТЕРА' + } + title = category_titles.get(cat_code, f'📞 СПИСОК ТЕЛЕФОНОВ: {cat_code.upper()}') + text = f"{title}\n\n" for s in services: text += f"🔹 {s.name}\n └ 📱 {s.phone}\n" if s.description: text += f" └ 📝 {s.description}\n" text += "\n" - # Если есть фото, придется прислать новое сообщение (edit_text не умеет менять текст на фото) - photo_path = services[0].image_path if services[0].image_path else None - - if photo_path and os.path.exists(photo_path): - await callback.message.delete() - await callback.message.answer_photo(FSInputFile(photo_path), caption=text, reply_markup=menu_kb, parse_mode='HTML') - else: - await safe_edit_or_reply(callback, text, reply_markup=menu_kb, parse_mode='HTML') + # Если есть фото у службы + photo_path = None + for s in services: + if s.image_path and os.path.exists(s.image_path): + photo_path = s.image_path + break + + await safe_edit_or_reply(callback, text, reply_markup=menu_kb, photo_path=photo_path, parse_mode='HTML') @router.message(Command('rules')) @router.callback_query(F.data == 'rules') diff --git a/keyboards/inline.py b/keyboards/inline.py index bc64e49..968544a 100644 --- a/keyboards/inline.py +++ b/keyboards/inline.py @@ -9,59 +9,62 @@ from utils.ui_styles import LEVEL_EMOJI def get_main_menu(verified: bool = False, is_ig: bool = False, is_admin: bool = False, is_superadmin: bool = False) -> InlineKeyboardMarkup: """ Стабильное главное меню LKM37 (Grid 2xN) + Строгая фильтрация по ролям: + - Гость: база + подтверждение квартиры + - Жилец: база + коммунальные сервисы дома + - Совет Дома: + Совет Дома + - Суперадмин: + Панель управления """ builder = InlineKeyboardBuilder() - # СТРОКА 1: БАЗА (ВСЕГДА ТУТ) + # СТРОКА 1: БАЗА (ДОСТУПНА ВСЕМ) builder.row( - InlineKeyboardButton(text='📞 Важные телефоны', callback_data='phones'), + InlineKeyboardButton(text='📞 Экстренные службы', callback_data='phones'), InlineKeyboardButton(text='⚖️ Юрист (ЖК РФ)', callback_data='main_jurist') ) - builder.row( - InlineKeyboardButton(text='🏢 Паспорт дома', callback_data='about_house') - ) - # СТРОКА 2-3: ДЛЯ ВЕРИФИЦИРОВАННЫХ (ФИКСИРОВАННОЕ МЕСТО) if verified: + # ДЛЯ ВЕРИФИЦИРОВАННЫХ ЖИЛЬЦОВ builder.row( InlineKeyboardButton(text='⚠️ Отключения', callback_data='schedule'), InlineKeyboardButton(text='💳 Оплата ЖКХ', callback_data='payments') ) builder.row( - InlineKeyboardButton(text='🗳️ Голосования', callback_data='polls'), - InlineKeyboardButton(text='🗓️ События дома', callback_data='events') + InlineKeyboardButton(text='🗳️ Опросы дома', callback_data='polls'), + InlineKeyboardButton(text='🏢 Паспорт дома', callback_data='about_house') ) + + # СОВЕТ ДОМА (ИНИЦИАТИВНАЯ ГРУППА) + if is_ig: + builder.row( + InlineKeyboardButton(text='👥 Совет Дома', callback_data='ig_menu') + ) + + # АДМИНИСТРИРОВАНИЕ (ТОЛЬКО ДЛЯ АДМИНОВ / СУПЕРАДМИНА) + if is_superadmin: + builder.row( + InlineKeyboardButton(text='🛡️ Панель управления', callback_data='superadmin_menu') + ) + elif is_admin: + builder.row( + InlineKeyboardButton(text='⚙️ Управление', callback_data='admin_menu') + ) + + # СЛУЖЕБНОЕ ВНИЗУ builder.row( - InlineKeyboardButton(text='📢 Объявления', callback_data='market'), - InlineKeyboardButton(text='🤝 Добрые соседи', callback_data='achievements') + InlineKeyboardButton(text='👤 Моя квартира', callback_data='my_profile'), + InlineKeyboardButton(text='❓ Помощь', callback_data='help') ) else: - # Для гостей кнопка верификации на видном месте + # ДЛЯ НЕВЕРИФИЦИРОВАННЫХ (ГОСТЕЙ) builder.row( + InlineKeyboardButton(text='🏢 Паспорт дома', callback_data='about_house'), InlineKeyboardButton(text='🔑 Подтвердить квартиру', callback_data='start_verification') ) - - # СТРОКА 4: ИНИЦИАТИВНАЯ ГРУППА И АДМИН - if is_ig or is_admin: - buttons = [] - if is_ig: - buttons.append(InlineKeyboardButton(text='👥 Совет Дома', callback_data='ig_menu')) - if is_admin: - buttons.append(InlineKeyboardButton(text='⚙️ Управление', callback_data='admin_menu')) - builder.row(*buttons) - - # СТРОКА 5: СУПЕР-АДМИН (ПРЕДСЕДАТЕЛЬ) - if is_superadmin: builder.row( - InlineKeyboardButton(text='🛡️ Панель председателя', callback_data='superadmin_menu') + InlineKeyboardButton(text='❓ Помощь / Связь с админом', callback_data='help') ) - # СТРОКА 6: СЛУЖЕБНОЕ (НИЗ) - builder.row( - InlineKeyboardButton(text='❓ Справка', callback_data='help'), - InlineKeyboardButton(text='👤 Моя квартира', callback_data='my_profile') - ) - return builder.as_markup() @@ -160,15 +163,18 @@ def get_phones_menu() -> InlineKeyboardMarkup: builder = InlineKeyboardBuilder() builder.row( - InlineKeyboardButton(text='🚨 Экстренные', callback_data='phones_emergency'), - InlineKeyboardButton(text='🔧 ЖКХ', callback_data='phones_utility'), + InlineKeyboardButton(text='⚡ Аварийка (54-69-29)', callback_data='phones_avariyka'), + InlineKeyboardButton(text='🚨 Все экстренные', callback_data='phones_emergency'), ) builder.row( - InlineKeyboardButton(text='👮 Полиция', callback_data='phones_police'), - InlineKeyboardButton(text='👷 Мастера', callback_data='phones_masters'), + InlineKeyboardButton(text='🔧 Диспетчер ЖКХ', callback_data='phones_utility'), + InlineKeyboardButton(text='👮 Полиция / Участковый', callback_data='phones_police'), ) builder.row( - InlineKeyboardButton(text='🔙 Назад', callback_data='main_menu'), + InlineKeyboardButton(text='👷 Мастера (сантехник и др.)', callback_data='phones_masters'), + ) + builder.row( + InlineKeyboardButton(text='🔙 Назад в меню', callback_data='main_menu'), ) return builder.as_markup() diff --git a/main.py b/main.py index 6ad6378..26ab3c3 100644 --- a/main.py +++ b/main.py @@ -50,14 +50,48 @@ async def on_startup(bot: Bot): scheduler = Scheduler(bot) scheduler.start() try: - from aiogram.types import BotCommand + from aiogram.types import ( + BotCommand, BotCommandScopeDefault, + BotCommandScopeAllGroupChats, BotCommandScopeAllPrivateChats, + BotCommandScopeChat + ) + + # 1. Для всех групповых чатов (публичный чат дома) - только базовая справка, без админок + await bot.set_my_commands([ + BotCommand(command="phones", description="📞 Экстренные службы (в ЛС)"), + BotCommand(command="help", description="❓ Справка (в ЛС)") + ], scope=BotCommandScopeAllGroupChats()) + + # 2. Для всех личных чатов с жильцами await bot.set_my_commands([ BotCommand(command="start", description="Главное меню дома"), + BotCommand(command="phones", description="📞 Экстренные службы"), BotCommand(command="jurist", description="⚖️ Юрист & Разбор ЖК РФ"), - BotCommand(command="admin", description="🛡️ Админ-панель"), - BotCommand(command="phones", description="📞 Экстренные телефоны"), BotCommand(command="help", description="❓ Помощь и навигация") - ]) + ], scope=BotCommandScopeAllPrivateChats()) + + # 3. Персонально для суперадмина дома (Александр Тырин) + if ADMIN_USER_ID: + try: + await bot.set_my_commands([ + BotCommand(command="admin", description="🛡️ Админ-панель дома"), + BotCommand(command="start", description="Главное меню дома"), + BotCommand(command="phones", description="📞 Экстренные службы"), + BotCommand(command="jurist", description="⚖️ Юрист & Разбор ЖК РФ"), + BotCommand(command="stats", description="📊 Статистика дома"), + BotCommand(command="help", description="❓ Помощь и навигация") + ], scope=BotCommandScopeChat(chat_id=ADMIN_USER_ID)) + except Exception as admin_cmd_err: + logger.warning(f"Failed to set admin commands scope: {admin_cmd_err}") + + # 4. Дефолтный fallback + await bot.set_my_commands([ + BotCommand(command="start", description="Главное меню дома"), + BotCommand(command="phones", description="📞 Экстренные телефоны"), + BotCommand(command="help", description="❓ Помощь") + ], scope=BotCommandScopeDefault()) + + logger.info("✅ Telegram BotCommand scopes successfully updated.") except Exception as e: logger.warning(f"Failed to set bot commands: {e}") diff --git a/utils/redirect.py b/utils/redirect.py new file mode 100644 index 0000000..c7752a4 --- /dev/null +++ b/utils/redirect.py @@ -0,0 +1,169 @@ +""" +Универсальный шлюз перенаправления в личные сообщения (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))