diff --git a/lk/handlers.py b/lk/handlers.py index 289f954..24397d3 100644 --- a/lk/handlers.py +++ b/lk/handlers.py @@ -57,7 +57,7 @@ EMOJI_DIGITS = { "10": "🔟" } -# Список из 7 справок строго по структуру портала +# Список из 7 справок строго по структуре портала DOC_TYPES = { "1": "Справка о доходах физического лица (2-НДФЛ)", "2": "Справка об удержаниях за ДМС", @@ -74,7 +74,7 @@ LK_DOCS_MENU_TEXT = ( f"\n\n{EMOJI_DIGITS['9']} — Назад\n{EMOJI_DIGITS['0']} — В главное меню" ) -USER_COOLDOWN_SEC = 1.5 +USER_COOLDOWN_SEC = 0.5 EMAIL_COOLDOWN_SEC = 10.0 user_last_request = {} @@ -129,18 +129,17 @@ async def handle_lk_error(msg: Message, user_id: str, action: str, error_details # ИНТЕГРАЦИЯ С ACTIVE DIRECTORY ЧЕРЕЗ extensionAttribute2 # ========================================================= def get_card_id_from_ad(user_id: str) -> str: - # Поиск CARD_ID по всем OU из AD_BASES from utils.ad_search import search_by_user_id try: - entries = search_by_user_id(user_id, ['extensionAttribute2']) - if not entries: - raise Exception("Пользователь не найден в AD.") - entry = entries[0] - if 'extensionAttribute2' in entry and entry.extensionAttribute2.value: - return str(entry.extensionAttribute2.value).strip() + search_id = user_id.replace("@tcs.sibcem.ru", "@sibcem.ru") + entries = search_by_user_id(search_id, attributes=['extensionAttribute2']) + + if entries and 'extensionAttribute2' in entries[0] and entries[0].extensionAttribute2.value: + return str(entries[0].extensionAttribute2.value).strip() + raise Exception("Поле extensionAttribute2 не заполнено в AD.") except Exception as e: - raise Exception("AD Card Query Error: {}".format(e)) + raise Exception(f"AD Card Query Error: {e}") # ========================================================= # МОДНОЕ УНИВЕРСАЛЬНОЕ ЯДРО ПАГИНАЦИИ СПИСКОВ ДЛЯ ЧАТА @@ -205,12 +204,25 @@ async def send_dynamic_lk_main_menu(msg: Message, user_id: str): await msg.answer(menu_text, parse_mode="html") async def send_tabel_months_menu(msg: Message, user_id: str): - now = datetime(2026, 6, 3) - prev_1 = now.replace(day=1) - timedelta(days=1) - prev_2 = prev_1.replace(day=1) - timedelta(days=1) - months_ru = {1: "Январь", 2: "Февраль", 3: "Март", 4: "Апрель", 5: "Май", 6: "Июнь", 7: "Июль", 8: "Август", 9: "Сентябрь", 10: "Октябрь", 11: "Ноябрь", 12: "Декабрь"} + now = datetime.now() + m1 = now.replace(day=1) - timedelta(days=1) # Прошлый месяц (M-1) + m2 = m1.replace(day=1) - timedelta(days=1) # Позапрошлый месяц (M-2) + + months_ru = { + 1: "Январь", 2: "Февраль", 3: "Март", 4: "Апрель", 5: "Май", 6: "Июнь", + 7: "Июль", 8: "Август", 9: "Сентябрь", 10: "Октябрь", 11: "Ноябрь", 12: "Декабрь" + } + + m1_str = f"{months_ru[m1.month]} {m1.year} г." + m2_str = f"{months_ru[m2.month]} {m2.year} г." + + menu_text = ( + "⏱ Выберите месяц для просмотра табеля:\n\n" + f"{EMOJI_DIGITS['1']} — {m1_str}\n" + f"{EMOJI_DIGITS['2']} — {m2_str}\n\n" + f"{EMOJI_DIGITS['9']} — Назад\n{EMOJI_DIGITS['0']} — В главное меню" + ) - menu_text = lk_tabel_months_menu_text(prev_1, prev_2, months_ru, EMOJI_DIGITS) set_state(user_id, "LK_TABEL_MONTHS") await msg.answer(menu_text, parse_mode="html") @@ -266,13 +278,22 @@ async def build_confirmation_text(user_id: str, doc: dict) -> str: async def lk_router_handler(msg: Message): user_id = msg.from_user.id current_time = time.time() - if current_time - user_last_request.get(user_id, 0) < USER_COOLDOWN_SEC: return + current_state = get_state(user_id) + + # Если пользователь находится внутри меню ЛК, помечаем сообщение обработанным, + # чтобы оно не улетало в ServiceDesk / Instruct роутеры + if current_state and current_state.startswith("LK_"): + msg.handled = True + + # Проверка на слишком частые клики (кулдаун) + if current_time - user_last_request.get(user_id, 0) < USER_COOLDOWN_SEC: + return user_last_request[user_id] = current_time - current_state = get_state(user_id) nav_text = lk_nav_text(EMOJI_DIGITS) - if not msg.text: return + if not msg.text: + return cmd = msg.text.strip().lower() for raw_num, emoji_num in EMOJI_DIGITS.items(): @@ -371,7 +392,6 @@ async def lk_router_handler(msg: Message): set_state(user_id, "LK_DOCS_MENU") await msg.answer(LK_DOCS_MENU_TEXT, parse_mode="html") else: - # 🎯 ИСПРАВЛЕНО: Вместо спама копии меню ЛК выводим строгое предупреждение await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") elif current_state == "LK_TABEL_MONTHS": @@ -384,16 +404,28 @@ async def lk_router_handler(msg: Message): set_state(user_id, "LK_MODE") await send_dynamic_lk_main_menu(msg, user_id) elif cmd in ["1", "2"]: - log_menu_stats(user_id, "Личный кабинет", "Просмотр табеля за месяц") + log_menu_stats(user_id, "Личный кабинет", f"Просмотр табеля (вариант {cmd})") await msg.answer(lk_tabel_loading(EMOJI_DIGITS), parse_mode="html") try: card_id = await asyncio.to_thread(get_card_id_from_ad, user_id) - now = datetime(2026, 6, 3) - target_date = now.replace(day=1) - timedelta(days=1) if cmd == "1" else (now.replace(day=1) - timedelta(days=1)).replace(day=1) - timedelta(days=1) - tabel_report = await asyncio.to_thread(get_tabel_report_text, card_id, target_date.year, target_date.month) + now = datetime.now() + + m1 = now.replace(day=1) - timedelta(days=1) + if cmd == "1": + target_date = m1 # Прошлый месяц + elif cmd == "2": + target_date = m1.replace(day=1) - timedelta(days=1) # Позапрошлый месяц + + tabel_report = await asyncio.to_thread( + get_tabel_report_text, + card_id, + target_date.year, + target_date.month + ) set_state(user_id, "LK_TABEL_VIEW") await msg.answer(tabel_report + nav_text, parse_mode="html") - except Exception as e: await handle_lk_error(msg, user_id, "Формирование табеля", str(e)) + except Exception as e: + await handle_lk_error(msg, user_id, "Формирование табеля", str(e)) else: await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") @@ -577,7 +609,6 @@ async def lk_router_handler(msg: Message): set_state(user_id, "LK_DOCS_QUANTITY") await msg.answer(lk_docs_quantity_prompt(EMOJI_DIGITS), parse_mode="html") else: - # 🎯 ИСПРАВЛЕНО: Заменен спам списка документов на чистую ошибку ввода await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") elif current_state == "LK_DOCS_QUANTITY": @@ -605,7 +636,6 @@ async def lk_router_handler(msg: Message): confirm_txt = await build_confirmation_text(user_id, user_docs_session[user_id]) await msg.answer(confirm_txt, parse_mode="html") else: - # 🎯 ИСПРАВЛЕНО: Вместо кастомной подсказки выводим единый стандарт await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") elif current_state == "LK_DOCS_PERIOD": @@ -618,14 +648,12 @@ async def lk_router_handler(msg: Message): if cmd in ["1", "2", "3", "4", "5", "6", "7", "8"]: user_docs_session[user_id]['period'] = f"за {datetime.now().year - (int(cmd) - 1)} год" else: - # 🎯 ИСПРАВЛЕНО: Убран спам меню годов при абракадабре await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") return elif doc_id == "5": if cmd == "1": user_docs_session[user_id]['period'] = "до 1.5 лет" elif cmd == "2": user_docs_session[user_id]['period'] = "до 3 лет" else: - # 🎯 ИСПРАВЛЕНО: Убран спам меню возраста при некорректном вводе await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") return elif doc_id == "3": @@ -669,7 +697,6 @@ async def lk_router_handler(msg: Message): except Exception as e: await handle_lk_error(msg, user_id, f"Заказ справки {user_docs_session.get(user_id, {}).get('doc_id')}", str(e)); set_state(user_id, "LK_MODE") finally: user_docs_session.pop(user_id, None) else: - # 🎯 ИСПРАВЛЕНО: Удален спам-текст подтверждения await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") elif current_state == "LK_DOCS_SUCCESS": @@ -677,5 +704,4 @@ async def lk_router_handler(msg: Message): 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, "LK_DOCS_MENU"); await msg.answer(LK_DOCS_MENU_TEXT, parse_mode="html") else: - # 🎯 ИСПРАВЛЕНО: Унифицировано под общий шаблон ошибки await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") \ No newline at end of file