feat: add Trueconf access request flow with intermediate prompt and existing ticket check

This commit is contained in:
Денис Кривоченко
2026-07-27 23:42:14 +07:00
parent 354c6b0a51
commit 9b65ec1e7e
2 changed files with 70 additions and 27 deletions
+50 -22
View File
@@ -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":
+20 -5
View File
@@ -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"<i>{emojis['9']} — Назад\n{emojis['0']} — В главное меню</i>"
)
def _instruct_trueconf_no_ticket(emojis: dict, cn: str) -> str:
"""Текст Trueconf когда пользователь не найден в AD."""
return (
f"🖥 <b>Инструкция по Trueconf</b>\n\n"
f"Пользователь {cn} не найден в Active Directory.\n\n"
f"<i>{emojis['9']} — Назад\n{emojis['0']} — В главное меню</i>"
)
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🎫 <b>Заявка #{ticket_id} создана!</b>" if ticket_id else ""
return (
f"🖥 <b>Инструкция по Trueconf</b>\n\n"
f"Пользователь {cn} не найден в Active Directory.{ticket_part}\n\n"
f"<i>{emojis['9']} — Назад\n{emojis['0']} — В главное меню</i>"
)
return _instruct_trueconf_no_ticket(emojis, cn)
if result["in_group"]:
body = (