Обновить instruct/handlers.py
This commit is contained in:
+37
-21
@@ -116,10 +116,33 @@ async def send_2fa_alert_email(cn: str, user_id: str, ticket_id: str):
|
|||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Запускаем отправку в отдельном потоке, чтобы не блокировать event loop asyncio
|
|
||||||
await asyncio.to_thread(_send_email_sync, ALERTS_SUPPORT_EMAILS, subject, body_html)
|
await asyncio.to_thread(_send_email_sync, ALERTS_SUPPORT_EMAILS, subject, body_html)
|
||||||
|
|
||||||
|
|
||||||
|
# ВСПОМОГАТЕЛЬНАЯ ФУНКЦИЯ ПРОВЕРКИ ЗАКРЫТОГО СТАТУСА ЗАЯВКИ
|
||||||
|
|
||||||
|
def _is_ticket_closed(status_data) -> bool:
|
||||||
|
"""
|
||||||
|
Проверяет, является ли статус заявки закрытым / исполненным.
|
||||||
|
Использует поиск по основам слов для улавливания всех форматов и склонений.
|
||||||
|
"""
|
||||||
|
if not status_data:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if isinstance(status_data, dict):
|
||||||
|
status_str = str(status_data.get("name", "")).strip().lower()
|
||||||
|
else:
|
||||||
|
status_str = str(status_data).strip().lower()
|
||||||
|
|
||||||
|
closed_keywords = [
|
||||||
|
"closed", "close", "resolved", "resolv", "solved", "solv",
|
||||||
|
"rejected", "reject", "canceled", "cancel", "completed", "complete",
|
||||||
|
"закрыт", "решен", "исполн", "выполн", "отмен", "отклон", "заверш"
|
||||||
|
]
|
||||||
|
|
||||||
|
return any(kw in status_str for kw in closed_keywords)
|
||||||
|
|
||||||
|
|
||||||
# ОСНОВНАЯ ЛОГИКА АКТИВНОСТИ
|
# ОСНОВНАЯ ЛОГИКА АКТИВНОСТИ
|
||||||
|
|
||||||
def get_user_cn(user_id: str) -> str | None:
|
def get_user_cn(user_id: str) -> str | None:
|
||||||
@@ -241,13 +264,6 @@ async def _find_existing_trueconf_ticket(cn: str, requester_email: str = None) -
|
|||||||
"""Находит открытую или находящуюся в работе заявку по Trueconf."""
|
"""Находит открытую или находящуюся в работе заявку по Trueconf."""
|
||||||
subject_keyword = "запрос доступа к trueconf"
|
subject_keyword = "запрос доступа к trueconf"
|
||||||
|
|
||||||
CLOSED_STATUSES = {
|
|
||||||
"resolved", "rejected", "closed", "solved", "canceled", "cancelled",
|
|
||||||
"решена", "закрыта", "отклонена", "выполнена", "отменена", "закрыто", "решено",
|
|
||||||
# ManageEngine Service Desk Plus statuses
|
|
||||||
"исполнен", "исполнена", "исполнено", "выполнен", "выполнена", "выполнено",
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
url = f"{SD_URL.rstrip('/')}/api/v3/requests"
|
url = f"{SD_URL.rstrip('/')}/api/v3/requests"
|
||||||
headers = {"TECHNICIAN_KEY": SD_TOKEN}
|
headers = {"TECHNICIAN_KEY": SD_TOKEN}
|
||||||
@@ -278,24 +294,28 @@ async def _find_existing_trueconf_ticket(cn: str, requester_email: str = None) -
|
|||||||
if subject_keyword not in req_subject:
|
if subject_keyword not in req_subject:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
req_status = req.get("status", {})
|
|
||||||
status_name = (
|
|
||||||
req_status.get("name", "") if isinstance(req_status, dict) else str(req_status)
|
|
||||||
).strip().lower()
|
|
||||||
|
|
||||||
req_id = req.get("id")
|
req_id = req.get("id")
|
||||||
|
req_status = req.get("status")
|
||||||
|
|
||||||
if status_name in CLOSED_STATUSES:
|
# 1. Проверяем статус в общем списке
|
||||||
logger.info(f"Skipping closed ticket #{req_id} (status: '{status_name}')")
|
if _is_ticket_closed(req_status):
|
||||||
|
logger.info(f"Skipping closed ticket #{req_id} (list status: '{req_status}')")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info(f"Checking candidate open ticket #{req_id} (status: '{status_name}')")
|
logger.info(f"Checking candidate open ticket #{req_id}")
|
||||||
|
|
||||||
detail_url = f"{SD_URL.rstrip('/')}/api/v3/requests/{req_id}"
|
detail_url = f"{SD_URL.rstrip('/')}/api/v3/requests/{req_id}"
|
||||||
detail_resp = await client.get(detail_url, headers=headers, timeout=10.0)
|
detail_resp = await client.get(detail_url, headers=headers, timeout=10.0)
|
||||||
|
|
||||||
if detail_resp.status_code == 200:
|
if detail_resp.status_code == 200:
|
||||||
detail = detail_resp.json().get("request", {})
|
detail = detail_resp.json().get("request", {})
|
||||||
|
|
||||||
|
# 2. Повторно проверяем статус в деталях заявки
|
||||||
|
detail_status = detail.get("status")
|
||||||
|
if _is_ticket_closed(detail_status):
|
||||||
|
logger.info(f"Skipping closed ticket #{req_id} (detail status: '{detail_status}')")
|
||||||
|
continue
|
||||||
|
|
||||||
requester = detail.get("requester", {})
|
requester = detail.get("requester", {})
|
||||||
req_name = str(requester.get("name", "") or requester.get("display_name", "")).strip().lower()
|
req_name = str(requester.get("name", "") or requester.get("display_name", "")).strip().lower()
|
||||||
req_email = str(requester.get("email", "") or requester.get("email_id", "")).strip().lower()
|
req_email = str(requester.get("email", "") or requester.get("email_id", "")).strip().lower()
|
||||||
@@ -534,8 +554,4 @@ async def instruct_router_handler(msg: Message):
|
|||||||
set_state(user_id, "INSTRUCT_MODE")
|
set_state(user_id, "INSTRUCT_MODE")
|
||||||
await msg.answer(INSTRUCT_MAIN_MENU_TEXT, parse_mode="html")
|
await msg.answer(INSTRUCT_MAIN_MENU_TEXT, parse_mode="html")
|
||||||
else:
|
else:
|
||||||
await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html")
|
await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user