fix(bot): приоритет роутера legal_advisor, кнопка в меню и всеядные команды
This commit is contained in:
parent
d4f572cbb4
commit
0aac30187b
4 changed files with 63 additions and 110 deletions
|
|
@ -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(
|
||||
"⚖️ <b>ЮРИДИЧЕСКИЙ ПОМОЩНИК (ЖК РФ)</b>\n\n"
|
||||
"Напишите ваш вопрос для анализа на основе судебных дел и ЖК РФ.\n\n"
|
||||
"<i>Я проанализирую ваш запрос через NotebookLM.</i>",
|
||||
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(
|
||||
"⚖️ <b>ЮРИДИЧЕСКИЙ ПОМОЩНИК (ЖК РФ)</b>\n\n"
|
||||
"Напишите ваш вопрос для анализа на основе судебных дел и ЖК РФ.\n\n"
|
||||
"<i>Я проанализирую ваш запрос через NotebookLM.</i>",
|
||||
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(
|
||||
"⏳ <i>Отправляю запрос в NotebookLM, анализирую судебные материалы и законы (это займет около 10-15 секунд)...</i>",
|
||||
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
|
||||
|
||||
# Заменяем <br>, <br/>, <br /> на переносы строк
|
||||
clean_output = output.replace("<br>", "\n").replace("<br/>", "\n").replace("<br />", "\n")
|
||||
# Экранируем спецсимволы для parse_mode='HTML'
|
||||
safe_output = html.escape(clean_output)
|
||||
# Форматируем **жирный** текст -> <b>жирный</b>
|
||||
safe_output = re.sub(r'\*\*(.*?)\*\*', r'<b>\1</b>', safe_output, flags=re.DOTALL)
|
||||
# Форматируем [ссылка](url) -> <a href="\2">\1</a>
|
||||
safe_output = re.sub(r'\[(.*?)\]\((.*?)\)', r'<a href="\2">\1</a>', 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"⚖️ <b>Анализ NotebookLM (часть 1):</b>\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"⚖️ <b>Анализ NotebookLM:</b>\n\n{safe_output}", parse_mode='HTML')
|
||||
else:
|
||||
await status_msg.edit_text(f"❌ <b>Ошибка запроса к NotebookLM:</b>\n{error or output}")
|
||||
except Exception as e:
|
||||
await status_msg.edit_text(f"❌ <b>Внутренняя ошибка бота:</b> {str(e)}")
|
||||
finally:
|
||||
await state.clear()
|
||||
|
||||
|
||||
@router.message(F.text & ~F.text.startswith('/') & F.chat.type == 'private')
|
||||
|
|
|
|||
|
|
@ -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("⏳ <i>Анализирую нормативную базу дома...</i>", parse_mode='HTML')
|
||||
result = await legal_engine.query(prompt=inline_question, role=role)
|
||||
await status_msg.delete()
|
||||
if result.get("success"):
|
||||
header = f"⚖️ <b>Юридический ответ ({result.get('latency_sec')} сек):</b>"
|
||||
await send_split_message(message, header, result["text"])
|
||||
else:
|
||||
await message.answer(f"❌ <b>Ошибка:</b> {result.get('text')}", parse_mode='HTML')
|
||||
return
|
||||
|
||||
role_desc = {
|
||||
"admin": "👑 <b>Режим Стратегического Аналитика (Администратор)</b>",
|
||||
"lawyer": "⚖️ <b>Режим Судебного Юриста (Совет дома / Активист)</b>",
|
||||
"resident": "🏡 <b>Соседский Юридический Помощник</b>"
|
||||
}.get(role, "🏡 <b>Юридический Помощник</b>")
|
||||
|
||||
text = (
|
||||
f"{role_desc}\n\n"
|
||||
"Задайте ваш вопрос по ЖКХ, протечкам, тарифам, собраниям или спорам с управляющей компанией.\n\n"
|
||||
"🎙 <b>Вы можете написать текст или отправить голосовое сообщение!</b>\n"
|
||||
"<i>Я проанализирую обращение по базе законов РФ и материалам дома.</i>"
|
||||
)
|
||||
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": "👑 <b>Режим Стратегического Аналитика (Администратор)</b>",
|
||||
"lawyer": "⚖️ <b>Режим Судебного Юриста (Совет дома / Активист)</b>",
|
||||
|
|
@ -94,7 +129,7 @@ async def cmd_legal_advisor(message: Message, state: FSMContext):
|
|||
"🎙 <b>Вы можете написать текст или отправить голосовое сообщение!</b>\n"
|
||||
"<i>Я проанализирую обращение по базе законов РФ и судебным материалам дома.</i>"
|
||||
)
|
||||
await message.answer(text, parse_mode='HTML')
|
||||
await callback.message.answer(text, parse_mode='HTML')
|
||||
await state.set_state(LegalQuestionState.waiting_for_question)
|
||||
|
||||
# === ОБРАБОТКА ТЕКСТОВЫХ ВОПРОСОВ ===
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
)
|
||||
|
||||
|
|
|
|||
29
main.py
29
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, '✅ <b>LKM37 CORE v4.3 АКТИВИРОВАН</b>')
|
||||
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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue