From 9b65ec1e7e61036251587afbbd2351d31740eb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B5=D0=BD=D0=B8=D1=81=20=D0=9A=D1=80=D0=B8=D0=B2?= =?UTF-8?q?=D0=BE=D1=87=D0=B5=D0=BD=D0=BA=D0=BE?= Date: Mon, 27 Jul 2026 23:42:14 +0700 Subject: [PATCH] feat: add Trueconf access request flow with intermediate prompt and existing ticket check --- instruct/handlers.py | 72 ++++++++++++++++++++++++++++++-------------- utils/texts.py | 25 ++++++++++++--- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/instruct/handlers.py b/instruct/handlers.py index c6c5ce6..6219d3a 100644 --- a/instruct/handlers.py +++ b/instruct/handlers.py @@ -14,6 +14,7 @@ from utils.texts import ( INSTRUCT_MAIN_MENU_TEXT, INSTRUCT_TRUECONF_TEXT, INSTRUCT_TRUECONF_WITH_AD, + INSTRUCT_TRUECONF_PENDING_TEXT, INSTRUCT_EMAIL_TEXT, ) from utils.states import get_state, set_state, clear_state @@ -275,37 +276,41 @@ async def instruct_router_handler(msg: Message): await msg.answer(MENU_TEXT, parse_mode="html") elif cmd == "1": log_menu_stats(user_id, "Инструкции", "Trueconf") - set_state(user_id, "INSTRUCT_TRUECONF_VIEW") - # Получаем CN пользователя cn = get_user_cn(user_id) if cn is None: cn = user_id # fallback - # Проверяем наличие группы 2FA в AD перед созданием заявки + # Проверяем наличие группы 2FA в AD from utils.ad_checker import check_group_membership ad_check = check_group_membership(cn, "2FA") - ticket_id = None - # Создаём заявку в SD ТОЛЬКО если у пользователя НЕТ группы 2FA (или он не найден) - if not ad_check.get("in_group", False): - ticket_id = await create_sd_ticket_for_trueconf(user_id, cn) + if ad_check.get("in_group", False): + # Есть доступ — сразу показываем инструкцию + set_state(user_id, "INSTRUCT_TRUECONF_VIEW") + text = INSTRUCT_TRUECONF_WITH_AD(EMOJI_DIGITS, cn, None) + if os.path.isfile(TRUECONF_INSTRUCT_PATH): + await msg.bot.send_document( + chat_id=msg.chat_id, + file=FSInputFile(TRUECONF_INSTRUCT_PATH, file_name="Инструкция.docx"), + caption=text, + parse_mode="html" + ) + else: + await msg.answer(text, parse_mode="html") else: - logger.info(f"User {cn} already has 2FA group. SD ticket creation skipped.") - - # Показываем текст с результатом - text = INSTRUCT_TRUECONF_WITH_AD(EMOJI_DIGITS, cn, ticket_id) - if ticket_id is not None: - # Нет доступа — отправляем текст без документа - await msg.answer(text, parse_mode="html") - elif os.path.isfile(TRUECONF_INSTRUCT_PATH): - # Есть доступ — отправляем документ с caption - await msg.bot.send_document( - chat_id=msg.chat_id, - file=FSInputFile(TRUECONF_INSTRUCT_PATH, file_name="Инструкция.docx"), - caption=text, - parse_mode="html" - ) + # Нет доступа — проверяем, есть ли уже открытая заявка + ticket_id = await create_sd_ticket_for_trueconf(user_id, cn) + if ticket_id: + # Заявка уже существует — показываем информацию + set_state(user_id, "INSTRUCT_TRUECONF_VIEW") + text = INSTRUCT_TRUECONF_WITH_AD(EMOJI_DIGITS, cn, ticket_id) + await msg.answer(text, parse_mode="html") + else: + # Заявки нет — показываем промпт с запросом + set_state(user_id, "INSTRUCT_TRUECONF_PENDING") + text = INSTRUCT_TRUECONF_PENDING_TEXT(EMOJI_DIGITS, cn) + await msg.answer(text, parse_mode="html") elif cmd == "2": log_menu_stats(user_id, "Инструкции", "Почта") set_state(user_id, "INSTRUCT_EMAIL_VIEW") @@ -313,6 +318,29 @@ async def instruct_router_handler(msg: Message): else: await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") + elif current_state == "INSTRUCT_TRUECONF_PENDING": + msg.handled = True + if cmd == "0": + clear_state(user_id) + from utils.menu import MENU_TEXT + await msg.answer(MENU_TEXT, parse_mode="html") + elif cmd == "9": + set_state(user_id, "INSTRUCT_MODE") + await msg.answer(INSTRUCT_MAIN_MENU_TEXT, parse_mode="html") + elif cmd == "1": + # Запросить доступ + cn = get_user_cn(user_id) + if cn is None: + cn = user_id + ticket_id = await create_sd_ticket_for_trueconf(user_id, cn) + if ticket_id: + text = INSTRUCT_TRUECONF_WITH_AD(EMOJI_DIGITS, cn, ticket_id) + await msg.answer(text, parse_mode="html") + else: + await msg.answer("❌ Не удалось создать заявку. Попробуйте позже.", parse_mode="html") + else: + await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") + elif current_state in ["INSTRUCT_TRUECONF_VIEW", "INSTRUCT_EMAIL_VIEW"]: msg.handled = True if cmd == "0": diff --git a/utils/texts.py b/utils/texts.py index ed72b3a..6959ee8 100644 --- a/utils/texts.py +++ b/utils/texts.py @@ -606,6 +606,25 @@ def _instruct_trueconf(emojis: dict) -> str: ) +def _instruct_trueconf_pending(emojis: dict, cn: str) -> str: + """Промпт для запроса доступа к Trueconf (когда нет заявки).""" + return ( + f"ℹ У вас отсутствует доступ к TrueConf с мобильных устройств.\n\n" + f"Если Вам требуется доступ, то напишите цифру {emojis['1']}, либо выберите другие пункты меню.\n\n" + f"{emojis['1']} — Запросить доступ\n" + f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню" + ) + + +def _instruct_trueconf_no_ticket(emojis: dict, cn: str) -> str: + """Текст Trueconf когда пользователь не найден в AD.""" + return ( + f"🖥 Инструкция по Trueconf\n\n" + f"Пользователь {cn} не найден в Active Directory.\n\n" + f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню" + ) + + def _instruct_trueconf_with_ad(emojis: dict, cn: str, ticket_id: str = None) -> str: """ Текст Trueconf с проверкой группы 2FA. @@ -618,11 +637,7 @@ def _instruct_trueconf_with_ad(emojis: dict, cn: str, ticket_id: str = None) -> if not result["found"]: ticket_part = f"\n\n🎫 Заявка #{ticket_id} создана!" if ticket_id else "" - return ( - f"🖥 Инструкция по Trueconf\n\n" - f"Пользователь {cn} не найден в Active Directory.{ticket_part}\n\n" - f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню" - ) + return _instruct_trueconf_no_ticket(emojis, cn) if result["in_group"]: body = (