From e550e657fe431552a8c08daa5143e2e079c81cc6 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: Thu, 30 Jul 2026 17:21:32 +0700 Subject: [PATCH] feat: ask about previous mobile email access before showing instructions --- instruct/handlers.py | 57 ++++++++++++++++++++------ utils/states.py | 95 ++++++++++++++++++++++---------------------- utils/texts.py | 12 +++++- 3 files changed, 103 insertions(+), 61 deletions(-) diff --git a/instruct/handlers.py b/instruct/handlers.py index b23e4ff..467674d 100644 --- a/instruct/handlers.py +++ b/instruct/handlers.py @@ -19,6 +19,7 @@ from utils.texts import ( INSTRUCT_TRUECONF_WITH_AD, INSTRUCT_TRUECONF_PENDING_TEXT, INSTRUCT_EMAIL_AVAILABLE, + INSTRUCT_EMAIL_HISTORY_ASK, INSTRUCT_EMAIL_UNAVAILABLE, INSTRUCT_ERROR_MSG, ) @@ -446,19 +447,10 @@ async def instruct_router_handler(msg: Message): ad_info = get_ad_user_info(user_id) raw_mail = ad_info.get("raw_mail") if ad_info else None if raw_mail: - text = INSTRUCT_EMAIL_AVAILABLE(EMOJI_DIGITS, cn, raw_mail) - # Отправляем текст сообщения + # Пользователь с доступом к почте — спрашиваем про прошлый доступ + set_state(user_id, "INSTRUCT_EMAIL_HISTORY") + text = INSTRUCT_EMAIL_HISTORY_ASK(EMOJI_DIGITS, cn) await msg.answer(text, parse_mode="html") - # Затем отправляем оба документа отдельно - for instruct_path, file_name in [ - (GMAIL_ANDROID_INSTRUCT_PATH, "Инструкция Android.docx"), - (APPLE_INSTRUCT_PATH, "Инструкция Apple.docx"), - ]: - if os.path.isfile(instruct_path): - await msg.bot.send_document( - chat_id=msg.chat_id, - file=FSInputFile(instruct_path, file_name=file_name), - ) else: text = INSTRUCT_EMAIL_UNAVAILABLE(EMOJI_DIGITS, cn) await msg.answer(text, parse_mode="html") @@ -489,6 +481,45 @@ async def instruct_router_handler(msg: Message): else: await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") + elif current_state == "INSTRUCT_EMAIL_HISTORY": + 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": + # Да, был доступ — показываем инструкции + log_menu_stats(user_id, "Инструкции", "Почта (был доступ)") + cn = get_user_cn(user_id) + if cn is None: + cn = user_id + set_state(user_id, "INSTRUCT_EMAIL_VIEW") + text = INSTRUCT_EMAIL_AVAILABLE(EMOJI_DIGITS, cn, "") + await msg.answer(text, parse_mode="html") + for instruct_path, file_name in [ + (GMAIL_ANDROID_INSTRUCT_PATH, "Инструкция Android.docx"), + (APPLE_INSTRUCT_PATH, "Инструкция Apple.docx"), + ]: + if os.path.isfile(instruct_path): + await msg.bot.send_document( + chat_id=msg.chat_id, + file=FSInputFile(instruct_path, file_name=file_name), + ) + elif cmd == "2": + # Нет, не было доступа — показываем текст про заявку + log_menu_stats(user_id, "Инструкции", "Почта (не было доступа)") + cn = get_user_cn(user_id) + if cn is None: + cn = user_id + set_state(user_id, "INSTRUCT_EMAIL_VIEW") + text = INSTRUCT_EMAIL_UNAVAILABLE(EMOJI_DIGITS, cn) + await msg.answer(text, 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": @@ -499,4 +530,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") \ No newline at end of file + await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html") diff --git a/utils/states.py b/utils/states.py index b097ca6..e5408a0 100644 --- a/utils/states.py +++ b/utils/states.py @@ -1,47 +1,48 @@ -# /opt/trueconf_bot/utils/states.py -import time -import config.config as config - -user_states = {} -tc_bot = None # Общая переменная для экземпляра бота - -# --- Константы состояний --- -PHOTO_MODE = "PHOTO_MODE" -SD_MODE = "SD_MODE" -TRANSCRIPTION_MODE = "TRANSCRIPTION_MODE" -SEARCH_MODE = "SEARCH_MODE" - -def set_state(user_id, state): - """Фиксирует новое состояние пользователя и обновляет метку времени активности""" - user_states[user_id] = { - "state": state, - "last_active": time.time() - } - -def get_state(user_id): - """ - Возвращает текущее состояние пользователя. - Если время неактивности превысило лимит из config.py, сессия сбрасывается. - """ - if user_id in user_states: - session = user_states[user_id] - - # Динамически забираем значение таймаута из конфига (дефолт — 1800 сек / 30 мин) - session_timeout = getattr(config, 'SESSION_TIMEOUT', 1800) - - # Проверяем, сколько секунд прошло с момента последнего сообщения - if time.time() - session["last_active"] > session_timeout: - # Время вышло! Аппаратно сносим сессию навигации - del user_states[user_id] - return None - - # Если время не вышло, обновляем таймер (пользователь проявил активность) - session["last_active"] = time.time() - return session["state"] - - return None - -def clear_state(user_id): - """Принудительно очищает состояние пользователя (выход в корень)""" - if user_id in user_states: - del user_states[user_id] \ No newline at end of file +# /opt/trueconf_bot/utils/states.py +import time +import config.config as config + +user_states = {} +tc_bot = None # Общая переменная для экземпляра бота + +# --- Константы состояний --- +PHOTO_MODE = "PHOTO_MODE" +SD_MODE = "SD_MODE" +TRANSCRIPTION_MODE = "TRANSCRIPTION_MODE" +SEARCH_MODE = "SEARCH_MODE" +INSTRUCT_EMAIL_HISTORY = "INSTRUCT_EMAIL_HISTORY" + +def set_state(user_id, state): + """Фиксирует новое состояние пользователя и обновляет метку времени активности""" + user_states[user_id] = { + "state": state, + "last_active": time.time() + } + +def get_state(user_id): + """ + Возвращает текущее состояние пользователя. + Если время неактивности превысило лимит из config.py, сессия сбрасывается. + """ + if user_id in user_states: + session = user_states[user_id] + + # Динамически забираем значение таймаута из конфига (дефолт — 1800 сек / 30 мин) + session_timeout = getattr(config, 'SESSION_TIMEOUT', 1800) + + # Проверяем, сколько секунд прошло с момента последнего сообщения + if time.time() - session["last_active"] > session_timeout: + # Время вышло! Аппаратно сносим сессию навигации + del user_states[user_id] + return None + + # Если время не вышло, обновляем таймер (пользователь проявил активность) + session["last_active"] = time.time() + return session["state"] + + return None + +def clear_state(user_id): + """Принудительно очищает состояние пользователя (выход в корень)""" + if user_id in user_states: + del user_states[user_id] diff --git a/utils/texts.py b/utils/texts.py index 518cb89..3fab433 100644 --- a/utils/texts.py +++ b/utils/texts.py @@ -633,6 +633,15 @@ def _instruct_email_available(emojis: dict, cn: str, mail: str) -> str: f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню" ) + +def _instruct_email_history_ask(emojis: dict, cn: str) -> str: + """Вопрос о наличии ранее доступа к мобильной почте.""" + return ( + f"📧 Был ли у Вас ранее доступ к мобильной почте?\n\n" + f"Нажмите {emojis['1']} — да, был / {emojis['2']} — нет, не было\n\n" + f"{emojis['0']} — В главное меню" + ) + def _instruct_email_unavailable(emojis: dict, cn: str) -> str: """Текст когда почта недоступна.""" return ( @@ -649,7 +658,8 @@ INSTRUCT_TRUECONF_WITH_AD = _instruct_trueconf_with_ad INSTRUCT_TRUECONF_PENDING_TEXT = _instruct_trueconf_pending INSTRUCT_TRUECONF_NO_TICKET_TEXT = _instruct_trueconf_no_ticket INSTRUCT_EMAIL_AVAILABLE = _instruct_email_available +INSTRUCT_EMAIL_HISTORY_ASK = _instruct_email_history_ask INSTRUCT_EMAIL_UNAVAILABLE = _instruct_email_unavailable # Единая стандартная заглушка системной ошибки для модуля инструкций -INSTRUCT_ERROR_MSG = system_error_text(EMOJI_DIGITS, show_back=True) \ No newline at end of file +INSTRUCT_ERROR_MSG = system_error_text(EMOJI_DIGITS, show_back=True)