diff --git a/handlers/admin.py b/handlers/admin.py index 48ad006..dce5398 100644 --- a/handlers/admin.py +++ b/handlers/admin.py @@ -58,104 +58,8 @@ async def process_swarm_task(message: Message, state: FSMContext): # ... (код остается прежним) pass -# ============================================================================= -# ЮРИДИЧЕСКИЙ ПОМОЩНИК (NotebookLM) -# ============================================================================= +# (Устаревший блок NotebookLM перенесен в специализированный модуль handlers/legal_advisor.py) -@router.message(Command('jurist')) -@router.message(F.text == '⚖️ Юрист (ЖК РФ)') -@admin_only -async def cmd_jurist(message: Message, state: FSMContext): - """Активация юридического помощника""" - await message.answer( - "⚖️ ЮРИДИЧЕСКИЙ ПОМОЩНИК (ЖК РФ)\n\n" - "Напишите ваш вопрос для анализа на основе судебных дел и ЖК РФ.\n\n" - "Я проанализирую ваш запрос через NotebookLM.", - parse_mode='HTML' - ) - await state.set_state(JuristState.waiting_for_question) - -@router.callback_query(F.data == 'admin_jurist') -@admin_callback_only -async def cb_admin_jurist(callback: CallbackQuery, state: FSMContext): - """Callback-активация юридического помощника""" - await callback.answer() - await callback.message.answer( - "⚖️ ЮРИДИЧЕСКИЙ ПОМОЩНИК (ЖК РФ)\n\n" - "Напишите ваш вопрос для анализа на основе судебных дел и ЖК РФ.\n\n" - "Я проанализирую ваш запрос через NotebookLM.", - parse_mode='HTML' - ) - await state.set_state(JuristState.waiting_for_question) - -@router.message(JuristState.waiting_for_question) -@admin_only -async def process_jurist_question(message: Message, state: FSMContext): - """Обработка вопроса к юристу через NotebookLM""" - status_msg = await message.answer( - "⏳ Отправляю запрос в NotebookLM, анализирую судебные материалы и законы (это займет около 10-15 секунд)...", - parse_mode='HTML' - ) - - import asyncio - import os - - # Динамический поиск бинарника nlm - nlm_bin = "nlm" - for p in ["/app/bin/nlm", "/usr/local/bin/nlm", os.path.expanduser("~/.local/bin/nlm"), "/home/matrixhasyou/.local/bin/nlm"]: - if os.path.exists(p): - nlm_bin = p - break - - cmd = [ - nlm_bin, "query", "notebook", - "00ed7d92-1ab2-4159-ac72-1d0c4be21377", - message.text - ] - env = os.environ.copy() - - try: - proc = await asyncio.create_subprocess_exec( - *cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - env=env - ) - stdout, stderr = await proc.communicate() - - output = stdout.decode('utf-8').strip() - error = stderr.decode('utf-8').strip() - - # Убираем предупреждения о новой версии из вывода nlm - if "Update available:" in output: - output = output.split("Update available:")[0].strip() - - if proc.returncode == 0: - import html - import re - - # Заменяем
,
,
на переносы строк - clean_output = output.replace("
", "\n").replace("
", "\n").replace("
", "\n") - # Экранируем спецсимволы для parse_mode='HTML' - safe_output = html.escape(clean_output) - # Форматируем **жирный** текст -> жирный - safe_output = re.sub(r'\*\*(.*?)\*\*', r'\1', safe_output, flags=re.DOTALL) - # Форматируем [ссылка](url) -> \1 - safe_output = re.sub(r'\[(.*?)\]\((.*?)\)', r'\1', safe_output, flags=re.DOTALL) - - if len(safe_output) > 4000: - parts = [safe_output[i:i+4000] for i in range(0, len(safe_output), 4000)] - await status_msg.edit_text(f"⚖️ Анализ NotebookLM (часть 1):\n\n{parts[0]}", parse_mode='HTML') - for part in parts[1:]: - await message.answer(part, parse_mode='HTML') - else: - await status_msg.edit_text(f"⚖️ Анализ NotebookLM:\n\n{safe_output}", parse_mode='HTML') - else: - await status_msg.edit_text(f"❌ Ошибка запроса к NotebookLM:\n{error or output}") - except Exception as e: - await status_msg.edit_text(f"❌ Внутренняя ошибка бота: {str(e)}") - finally: - await state.clear() @router.message(F.text & ~F.text.startswith('/') & F.chat.type == 'private') diff --git a/handlers/legal_advisor.py b/handlers/legal_advisor.py index c36946d..74ce5eb 100644 --- a/handlers/legal_advisor.py +++ b/handlers/legal_advisor.py @@ -77,11 +77,46 @@ async def determine_user_role(user_id: int) -> str: # === КОМАНДЫ АКТИВАЦИИ === -@router.message(Command("юрист", "jurist", "вопрос", "help_law")) -@router.message(F.text == "⚖️ Юрист (ЖК РФ)") -async def cmd_legal_advisor(message: Message, state: FSMContext): +@router.message(Command("jurist", "юрист", "вопрос", "help_law")) +@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): role = await determine_user_role(message.from_user.id) + # Проверяем, не задал ли пользователь вопрос прямо в команде: /юрист Какой срок ответа? + parts = (message.text or "").split(maxsplit=1) + if len(parts) > 1 and parts[0].casefold() in ("/юрист", "/jurist", "/вопрос"): + inline_question = parts[1].strip() + if len(inline_question) > 3: + status_msg = await message.answer("⏳ Анализирую нормативную базу дома...", parse_mode='HTML') + result = await legal_engine.query(prompt=inline_question, role=role) + await status_msg.delete() + if result.get("success"): + header = f"⚖️ Юридический ответ ({result.get('latency_sec')} сек):" + await send_split_message(message, header, result["text"]) + else: + await message.answer(f"❌ Ошибка: {result.get('text')}", parse_mode='HTML') + return + + role_desc = { + "admin": "👑 Режим Стратегического Аналитика (Администратор)", + "lawyer": "⚖️ Режим Судебного Юриста (Совет дома / Активист)", + "resident": "🏡 Соседский Юридический Помощник" + }.get(role, "🏡 Юридический Помощник") + + text = ( + f"{role_desc}\n\n" + "Задайте ваш вопрос по ЖКХ, протечкам, тарифам, собраниям или спорам с управляющей компанией.\n\n" + "🎙 Вы можете написать текст или отправить голосовое сообщение!\n" + "Я проанализирую обращение по базе законов РФ и материалам дома." + ) + await message.answer(text, parse_mode='HTML') + await state.set_state(LegalQuestionState.waiting_for_question) + +@router.callback_query(F.data.in_(["admin_jurist", "jurist_ai", "main_jurist"])) +async def cb_legal_advisor(callback: CallbackQuery, state: FSMContext): + await callback.answer() + role = await determine_user_role(callback.from_user.id) role_desc = { "admin": "👑 Режим Стратегического Аналитика (Администратор)", "lawyer": "⚖️ Режим Судебного Юриста (Совет дома / Активист)", @@ -94,7 +129,7 @@ async def cmd_legal_advisor(message: Message, state: FSMContext): "🎙 Вы можете написать текст или отправить голосовое сообщение!\n" "Я проанализирую обращение по базе законов РФ и судебным материалам дома." ) - await message.answer(text, parse_mode='HTML') + await callback.message.answer(text, parse_mode='HTML') await state.set_state(LegalQuestionState.waiting_for_question) # === ОБРАБОТКА ТЕКСТОВЫХ ВОПРОСОВ === diff --git a/keyboards/inline.py b/keyboards/inline.py index a03301a..dc488b6 100644 --- a/keyboards/inline.py +++ b/keyboards/inline.py @@ -15,6 +15,9 @@ def get_main_menu(verified: bool = False, is_ig: bool = False, is_admin: bool = # СТРОКА 1: БАЗА (ВСЕГДА ТУТ) builder.row( InlineKeyboardButton(text='📞 Телефоны', callback_data='phones'), + InlineKeyboardButton(text='⚖️ Юрист (ЖК РФ)', callback_data='main_jurist') + ) + builder.row( InlineKeyboardButton(text='🏢 О доме', callback_data='about_house') ) diff --git a/main.py b/main.py index d3c4426..6ad6378 100644 --- a/main.py +++ b/main.py @@ -49,6 +49,18 @@ async def on_startup(bot: Bot): await init_db() scheduler = Scheduler(bot) scheduler.start() + try: + from aiogram.types import BotCommand + await bot.set_my_commands([ + BotCommand(command="start", description="Главное меню дома"), + BotCommand(command="jurist", description="⚖️ Юрист & Разбор ЖК РФ"), + BotCommand(command="admin", description="🛡️ Админ-панель"), + BotCommand(command="phones", description="📞 Экстренные телефоны"), + BotCommand(command="help", description="❓ Помощь и навигация") + ]) + except Exception as e: + logger.warning(f"Failed to set bot commands: {e}") + try: await bot.send_message(ADMIN_USER_ID, '✅ LKM37 CORE v4.3 АКТИВИРОВАН') await send_domovoy_alert("Сервис запущен", "Домовой МКД успешно запущен в автономном коммунальном режиме.") @@ -59,7 +71,12 @@ async def main(): bot = get_bot() dp = Dispatcher(storage=MemoryStorage()) - # Регистрация роутеров МКД + # Регистрация роутеров МКД (Юрист в наивысшем приоритете) + from handlers.legal_advisor import router as legal_advisor_router + from handlers.smart_broadcast import router as smart_broadcast_router + from handlers.digest import router as digest_router + + dp.include_router(legal_advisor_router) dp.include_router(admin_router) dp.include_router(users_router) @@ -69,18 +86,12 @@ async def main(): antispam_router, ratelimit_router, schedule_router, ads_router, payments_router, multicount_router, topics_router, toxicity_router, profile_history_router, achievements_router, thanks_router, - events_router, initiative_group_router, mutual_aid_router + events_router, initiative_group_router, mutual_aid_router, + smart_broadcast_router, digest_router ] for r in other_routers: dp.include_router(r) - - from handlers.smart_broadcast import router as smart_broadcast_router - from handlers.digest import router as digest_router - from handlers.legal_advisor import router as legal_advisor_router - dp.include_router(legal_advisor_router) - dp.include_router(smart_broadcast_router) - dp.include_router(digest_router) dp.startup.register(on_startup)