From c5edcbdd0698b129a7bd560190bdd0c0e8283c0e Mon Sep 17 00:00:00 2001 From: dddennnisss Date: Thu, 30 Jul 2026 22:29:53 +0700 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20instruct/handlers.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- instruct/handlers.py | 58 ++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/instruct/handlers.py b/instruct/handlers.py index 8e26985..ea0a151 100644 --- a/instruct/handlers.py +++ b/instruct/handlers.py @@ -116,10 +116,33 @@ async def send_2fa_alert_email(cn: str, user_id: str, ticket_id: str): """ - # Запускаем отправку в отдельном потоке, чтобы не блокировать event loop asyncio 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: @@ -241,13 +264,6 @@ async def _find_existing_trueconf_ticket(cn: str, requester_email: str = None) - """Находит открытую или находящуюся в работе заявку по Trueconf.""" subject_keyword = "запрос доступа к trueconf" - CLOSED_STATUSES = { - "resolved", "rejected", "closed", "solved", "canceled", "cancelled", - "решена", "закрыта", "отклонена", "выполнена", "отменена", "закрыто", "решено", - # ManageEngine Service Desk Plus statuses - "исполнен", "исполнена", "исполнено", "выполнен", "выполнена", "выполнено", - } - try: url = f"{SD_URL.rstrip('/')}/api/v3/requests" 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: 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_status = req.get("status") - if status_name in CLOSED_STATUSES: - logger.info(f"Skipping closed ticket #{req_id} (status: '{status_name}')") + # 1. Проверяем статус в общем списке + if _is_ticket_closed(req_status): + logger.info(f"Skipping closed ticket #{req_id} (list status: '{req_status}')") 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_resp = await client.get(detail_url, headers=headers, timeout=10.0) if detail_resp.status_code == 200: 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", {}) 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() @@ -534,8 +554,4 @@ async def instruct_router_handler(msg: Message): set_state(user_id, "INSTRUCT_MODE") await msg.answer(INSTRUCT_MAIN_MENU_TEXT, parse_mode="html") else: - await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") - - - - + await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") \ No newline at end of file