# /opt/trueconf_bot/utils/texts.py
# Все текстовые сообщения бота, отправляемые пользователю
# =========================================================
# GLOBAL CONSTANTS & HELPERS
# =========================================================
# --- Main Bot ---
UNKNOWN_MAIN_CMD_TEXT = (
"⚠️ Неизвестная команда.\n\n"
"Пожалуйста, используйте только цифры, соответствующие нужным пунктам меню, и отправьте выбранную цифру мне в сообщении."
)
# --- Emoji Digits ---
EMOJI_DIGITS = {
"0": "0⃣",
"1": "1⃣",
"2": "2⃣",
"3": "3⃣",
"4": "4⃣",
"5": "5⃣",
"6": "6⃣",
"7": "7⃣",
"8": "8⃣",
"9": "9⃣",
"10": "🔟"
}
# --- Footer Templates ---
def _footer(emojis: dict) -> str:
"""Стандартный футер с кнопками Назад и Меню"""
return f"\n\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
def _footer_main_only(emojis: dict) -> str:
"""Футер только с кнопкой Меню"""
return f"\n\n{emojis['0']} — В главное меню"
# --- Universal System Error Stub ---
def system_error_text(emojis: dict = EMOJI_DIGITS, show_back: bool = True) -> str:
"""Универсальная системная заглушка ошибки для всех модулей бота."""
footer = _footer(emojis) if show_back else _footer_main_only(emojis)
return (
"⚠️ В данный момент выполнить это действие невозможно. "
"Информация о проблеме передана ответственным.\n"
f"Приносим извинения за доставленные неудобства.{footer}"
)
# =========================================================
# TRANSCRIPTION BOT
# =========================================================
# Модуль для расшифровки аудио и видео файлов
# --- Private functions (Internal logic) ---
def _transcription_welcome(emojis: dict) -> str:
return (
f"🎙 Добро пожаловать! Я — Ассистент расшифровки совещаний.\n\n"
f"Я помогу вам перевести аудио или видео в текст, а также подготовлю краткое резюме вашей встречи.\n\n"
f"📎 Для начала работы просто отправьте нужный файл прямо в этот чат.\n\n"
f"{emojis['0']} — В главное меню"
)
def _transcription_unknown(emojis: dict) -> str:
return (
f"⚠️ Неизвестная команда.\n\n"
f"Пожалуйста, отправьте в сообщении мне нужную цифру соответствующего пункта меню в чат."
)
def _transcription_bad_extension(ext: str, emojis: dict) -> str:
return (
f"❌ Ошибка: Недопустимый формат файла ({ext}).\n\n"
f"Пожалуйста, отправьте поддерживаемый аудио или видео файл.\n\n"
f"{_footer(emojis)}"
)
def _transcription_uploading() -> str:
return "⏳ Загружаю файл..."
def _transcription_success(email: str, emojis: dict) -> str:
return (
f"✅ Хорошо, файл принят в обработку.\n\n"
f"Как только всё будет готово, я отправлю вам результаты на почту ({email}).\n\n"
f"⏳ Обычно это занимает от 5 до 30 минут.\n\n"
f"{_footer(emojis)}"
)
def _transcription_api_error(status_code: int, emojis: dict) -> str:
return f"❌ Ошибка API сервера ({status_code}).{_footer(emojis)}"
def _transcription_system_error(emojis: dict) -> str:
return f"❌ Произошла системная ошибка при передаче файла.{_footer(emojis)}"
# --- Public constants and functions ---
TRANSCRIPTION_MAIN_MENU_TEXT = _transcription_welcome(EMOJI_DIGITS)
TRANSCRIPTION_UNKNOWN_CMD_TEXT = _transcription_unknown(EMOJI_DIGITS)
def transcription_error_bad_extension(ext: str) -> str:
return _transcription_bad_extension(ext, EMOJI_DIGITS)
TRANSCRIPTION_UPLOADING_TEXT = _transcription_uploading()
def transcription_success_received(email: str) -> str:
return _transcription_success(email, EMOJI_DIGITS)
def transcription_error_api(status_code: int) -> str:
return _transcription_api_error(status_code, EMOJI_DIGITS)
def transcription_error_system() -> str:
return _transcription_system_error(EMOJI_DIGITS)
# =========================================================
# SERVICE DESK BOT
# =========================================================
# Модуль для создания и управления заявками в Service Desk
# --- Private functions (Internal logic) ---
def _sd_welcome(emojis: dict) -> str:
return (
f"🎫 Режим создания заявки в Service Desk\n\n"
f"• Опишите проблему текстом.\n"
f"• Можно прикрепить фото или документ.\n\n"
f"{emojis['0']} — В главное меню"
)
def _sd_unknown(emojis: dict) -> str:
return (
f"⚠️ Неизвестная команда.\n"
f"Пожалуйста, используйте цифры меню из сообщения выше.\n\n"
f"{emojis['9']} — Назад\n"
f"{emojis['0']} — В главное меню"
)
def _sd_text_required(emojis: dict) -> str:
return f"⚠️ Сначала опишите проблему текстом."
def _sd_creating() -> str:
return "⏳ Создаю заявку, пожалуйста, подождите..."
def _sd_ticket_created(ticket_id: int, subject: str, description: str, emojis: dict) -> str:
clean_desc = description.replace("\n", " ").strip()
if len(clean_desc) > 150:
clean_desc = clean_desc[:147] + "..."
return (
f"✅ Заявка #{ticket_id} создана!\n\n"
f"📌 Тема: {subject}\n\n"
f"📝 Текст: {clean_desc}\n\n"
f"Если нужно добавить скриншоты или документ — отправьте их сейчас.\n\n"
f"{_footer(emojis)}"
)
def _sd_ticket_create_error(emojis: dict) -> str:
return f"❌ Ошибка создания заявки.{_footer_main_only(emojis)}"
def _sd_system_error(emojis: dict) -> str:
return f"❌ Системная ошибка. Попробуйте позже.{_footer_main_only(emojis)}"
def _sd_uploading_file(ticket_id: int, emojis: dict) -> str:
return f"⏳ Загружаю файл в заявку #{ticket_id}..."
def _sd_file_attached(emojis: dict) -> str:
return f"✅ Файл успешно прикреплен!{_footer(emojis)}"
def _sd_file_upload_error(emojis: dict) -> str:
return f"❌ Ошибка загрузки файла.{_footer_main_only(emojis)}"
# --- Public constants and functions ---
SD_MAIN_MENU_TEXT = _sd_welcome(EMOJI_DIGITS)
SD_UNKNOWN_CMD_TEXT = _sd_unknown(EMOJI_DIGITS)
SD_TEXT_REQUIRED = _sd_text_required(EMOJI_DIGITS)
SD_CREATING = _sd_creating()
def sd_ticket_created(ticket_id: int, subject: str, description: str) -> str:
return _sd_ticket_created(ticket_id, subject, description, EMOJI_DIGITS)
def sd_ticket_create_error() -> str:
return _sd_ticket_create_error(EMOJI_DIGITS)
def sd_system_error() -> str:
return _sd_system_error(EMOJI_DIGITS)
def sd_uploading_file(ticket_id: int) -> str:
return _sd_uploading_file(ticket_id, EMOJI_DIGITS)
def sd_file_attached() -> str:
return _sd_file_attached(EMOJI_DIGITS)
def sd_file_upload_error() -> str:
return _sd_file_upload_error(EMOJI_DIGITS)
# =========================================================
# PHOTO BOT
# =========================================================
# Модуль для обновления фото профиля — интеграция с Django API
# --- Private functions (Internal logic) ---
def _photo_welcome(emojis: dict, show_test: bool = False, info: str = None, pending: bool = False) -> str:
"""Входное сообщение фото-режима. info — необязательный блок о статусе
модерации (отказ). pending=True — фото уже на модерации: без призыва
отправить фото, только статус."""
text = f"🖼 Режим обновления фото профиля\n\n"
if pending:
text += (
f"⏳ У вас уже есть фото на модерации.\n"
f"Ожидайте решения модератора.\n\n"
)
else:
text += (
f"📸 Пришлите мне вашу фотографию.\n\n"
f"Бот автоматически найдёт лицо, покажет превью\n"
f"и отправит фото на публикацию в ваши корпоративные сервисы.\n\n"
)
if info:
text += info + "\n\n"
if show_test:
text += f"{emojis['1']} — тест проверка фото\n"
return text + f"{emojis['0']} — В главное меню"
def _photo_unknown(emojis: dict) -> str:
return (
f"⚠️ Неизвестная команда.\n"
f"Пожалуйста, используйте цифры меню из сообщения выше.\n\n"
f"{emojis['9']} — Назад\n"
f"{emojis['0']} — В главное меню"
)
def _photo_processing_face(emojis: dict) -> str:
return "⏳ Ищу лицо на фото, пожалуйста, подождите..."
def _photo_preview_ready(emojis: dict) -> str:
return (
f"📸 Вот ваш кадр.\n\n"
f"{emojis['1']} — Подтвердить\n"
f"{emojis['2']} — Вырезать заново\n\n"
f"{emojis['0']} — В главное меню"
)
def _photo_ai_processing(emojis: dict) -> str:
return (
"⏳ ИИ обрабатывает фото. Это может занять до 5 минут.\n"
"Пожалуйста, не закрывайте чат и подождите..."
)
def _photo_status_info(status: dict = None, emojis: dict = None) -> str | None:
"""Блок о статусе модерации для входного сообщения. Универсальный для всех
случаев: rejected → причина отказа, pending → ожидание, прочее → пусто."""
if not status or status.get("status") != "ok":
return None
ms = status.get("moderation_status")
if ms == "rejected":
reason = (status.get("rejection_reason") or "Причина не указана.").strip()
return (
f"⚠️ Ваше предыдущее фото было отклонено.\n"
f"📝 Причина: {reason}\n\n"
f"Пожалуйста, отправьте новое фото."
)
if ms == "pending":
return (
f"⏳ У вас уже есть фото на модерации.\n"
f"Ожидайте решения модератора."
)
return None
def _photo_test_welcome(emojis: dict) -> str:
"""Тест-приветствие: фото уйдёт напрямую в ИИ, никуда не публикуется."""
return (
f"🧪 Тестовый режим фото-проверки\n\n"
f"📸 Пришлите фотографию — она уйдёт напрямую в ИИ.\n"
f"Бот покажет вердикт ИИ. Фото никуда не публикуется.\n\n"
f"{emojis['9']} — Назад\n"
f"{emojis['0']} — В главное меню"
)
def _photo_test_preview(emojis: dict) -> str:
return (
f"🧪 Тестовый режим\n\n"
f"Кадр получен. Фото никуда не публикуется.\n\n"
f"{emojis['1']} — отправлю фото на проверку по сайту\n"
f"{emojis['2']} — Загрузить другое фото\n"
f"{emojis['9']} — Назад\n"
f"{emojis['0']} — В главное меню"
)
def _photo_test_ai_checking(emojis: dict) -> str:
return (
"🧪 Отправляю фото на проверку по сайту (тестовый режим)...\n"
"Фото проходят полный контроль: CV, гримасы, ИИ.\n"
"Обычно это занимает 1-3 минуты. Пожалуйста, подождите..."
)
def _photo_test_receiving(emojis: dict) -> str:
return "🧪 Ищу лицо на фото (тестовый режим), пожалуйста, подождите..."
def _photo_test_result(verdict: str, reason: str, cv_approved=None, cv_reason: str = '',
forced=None, timings: dict = None, ai_metrics: dict = None,
emojis: dict = None) -> str:
emojis = emojis or {}
icons = {"Прошел": "✅", "Не прошел": "❌", "На модерацию": "⏳"}
icon = icons.get(verdict, "ℹ️")
parts = [
"🧪 Тестовый режим: результат проверки\n",
f"{icon} Вердикт: {verdict}\n",
]
if reason:
parts.append(f"📝 Причина (ИИ): {reason}\n")
# Детерминированный слой (MediaPipe CV + ARKit-формы)
if cv_approved is True:
line = "🎯 CV-проверка: ✅ пройдена"
if cv_reason:
line += f" — {cv_reason}"
parts.append(line + "\n")
elif cv_approved is False:
parts.append("🎯 CV-проверка: ❌ отклонена\n")
if cv_reason:
parts.append(f"↳ {cv_reason}\n")
else:
parts.append("🎯 CV-проверка: —\n")
if forced:
parts.append("ℹ️ На сайте включён режим принудительной модерации: в реальном режиме фото ушло бы модератору, а не было опубликовано сразу.\n")
# Тайминги этапов
if timings:
names = [
('cv', "CV (геометрия)"),
('expression', "Гримасы (ARKit-формы)"),
('ai', "ИИ (llama.cpp)"),
('total', "Всего"),
]
tl = ["📊 Технические показатели (по этапам)"]
for k, name in names:
v = timings.get(k)
if v is not None:
tl.append(f"⏱ {name}: {float(v):.2f} сек")
if len(tl) > 1:
parts.append("\n".join(tl) + "\n")
# Токены и скорость (сайт передаёт их из ответа llama.cpp)
if ai_metrics:
ml = ["🔢 ИИ: токены и скорость"]
pt = ai_metrics.get('prompt_tokens')
gps = ai_metrics.get('prompt_per_second')
if pt is not None:
s = f"⬇️ Приём (промпт): {pt} токенов"
if gps is not None:
s += f" — {float(gps):.1f} ток/сек"
ml.append(s)
ct = ai_metrics.get('completion_tokens')
ggs = ai_metrics.get('predicted_per_second')
if ct is not None:
s = f"⬆️ Отдача (генерация): {ct} токенов"
if ggs is not None:
s += f" — {float(ggs):.1f} ток/сек"
ml.append(s)
if len(ml) > 1:
parts.append("\n".join(ml) + "\n")
parts.append("⚠️ Тестовый режим: фото не публиковалось и никуда не отправлялось.\n\n")
parts.append(f"{emojis.get('2', '2')} — Загрузить другое фото\n")
parts.append(f"{emojis.get('9', '9')} — Назад\n")
parts.append(f"{emojis.get('0', '0')} — В главное меню")
return "\n".join(parts)
def _photo_test_ai_error(emojis: dict, error: str = '', elapsed: float = None) -> str:
detail = ''
err = (error or '').strip()
if len(err) > 400:
err = err[:400] + '…'
if err:
detail += f"📋 Детали: {err}\n"
if elapsed is not None:
detail += f"⏱ Время до ошибки: {elapsed:.1f} сек\n"
return (
f"❌ Ошибка проверки фото.\n\n"
f"{detail}\n"
f"Сервис проверки временно недоступен. Повторите попытку позже.\n\n"
f"{emojis['1']} — Повторить проверку\n"
f"{emojis['2']} — Загрузить другое фото\n"
f"{emojis['0']} — В главное меню"
)
def _photo_success_published(emojis: dict) -> str:
return (
f"🎉 Фото успешно опубликовано!\n\n"
f"Применение во всех системах может занять до 2-х суток.\n\n"
f"{emojis['0']} — В главное меню"
)
def _photo_success_pending(emojis: dict) -> str:
return (
f"📋 Фото отправлено на модерацию.\n\n"
f"Ожидайте уведомления.\n\n"
f"{emojis['0']} — В главное меню"
)
def _photo_error_rejected(reason: str, emojis: dict) -> str:
return (
f"⚠️ Фото отклонено: {reason}\n\n"
f"Пожалуйста, отправьте другое фото.\n\n"
f"{emojis['0']} — В главное меню"
)
def _photo_error_already_pending(emojis: dict) -> str:
return (
f"⚠️ У вас уже есть фото на модерации.\n\n"
f"Подождите завершения текущей обработки.\n\n"
f"{emojis['0']} — В главное меню"
)
def _photo_cancel(emojis: dict) -> str:
return (
f"🚫 Отменено. Пришлите другое фото.\n\n"
f"{emojis['0']} — В главное меню"
)
# --- Public constants and functions ---
PHOTO_MAIN_MENU_TEXT = _photo_welcome(EMOJI_DIGITS)
PHOTO_TEST_ENTRY_TEXT = _photo_welcome(EMOJI_DIGITS, show_test=True)
PHOTO_UNKNOWN_CMD_TEXT = _photo_unknown(EMOJI_DIGITS)
def photo_error_handling() -> str:
return system_error_text(EMOJI_DIGITS, show_back=False)
PHOTO_PROCESSING_FACE_TEXT = _photo_processing_face(EMOJI_DIGITS)
def photo_preview_ready() -> str:
return _photo_preview_ready(EMOJI_DIGITS)
PHOTO_AI_PROCESSING_TEXT = _photo_ai_processing(EMOJI_DIGITS)
def photo_success_published() -> str:
return _photo_success_published(EMOJI_DIGITS)
def photo_success_pending() -> str:
return _photo_success_pending(EMOJI_DIGITS)
def photo_error_rejected(reason: str) -> str:
return _photo_error_rejected(reason, EMOJI_DIGITS)
def photo_error_already_pending() -> str:
return _photo_error_already_pending(EMOJI_DIGITS)
def photo_cancel_text() -> str:
return _photo_cancel(EMOJI_DIGITS)
def photo_welcome_with_status(status: dict = None, show_test: bool = False) -> str:
"""Универсальное входное сообщение фото-режима: меню + статус модерации.
pending — только статус (без призыва отправить фото);
show_test — добавить кнопку тест-проверки (PHOTO_TEST_MODE)."""
info = _photo_status_info(status, EMOJI_DIGITS)
pending = bool(
status and status.get("status") == "ok"
and status.get("moderation_status") == "pending"
)
return _photo_welcome(EMOJI_DIGITS, show_test=show_test, info=info, pending=pending)
PHOTO_TEST_WELCOME_TEXT = _photo_test_welcome(EMOJI_DIGITS)
def photo_test_welcome() -> str:
"""Тест-приветствие (тест-под-режим фото-проверки)."""
return _photo_test_welcome(EMOJI_DIGITS)
PHOTO_TEST_PREVIEW_TEXT = _photo_test_preview(EMOJI_DIGITS)
PHOTO_TEST_AI_CHECKING_TEXT = _photo_test_ai_checking(EMOJI_DIGITS)
PHOTO_TEST_RECEIVING_TEXT = _photo_test_receiving(EMOJI_DIGITS)
def photo_test_result(verdict: str, ai_reason: str = '', cv_approved=None, cv_reason: str = '',
forced=None, timings: dict = None, ai_metrics: dict = None) -> str:
return _photo_test_result(verdict, ai_reason, cv_approved, cv_reason,
forced, timings, ai_metrics, EMOJI_DIGITS)
def photo_test_ai_error(error: str = '') -> str:
return _photo_test_ai_error(EMOJI_DIGITS, error)
# =========================================================
# SEARCH BOT
# =========================================================
# Модуль поиска по регламентам
# --- Private functions (Internal logic) ---
def _search_welcome(emojis: dict) -> str:
return (
f"📚 Поиск по регламентам\n\n"
f"Введите ваш вопрос (например: 'время хранения электронных документов' ).\n"
f"Бот найдет наиболее подходящие абзацы из нормативных документов.\n\n"
f"Для возврата в главное меню отправьте {emojis['0']}."
)
def _search_unknown(emojis: dict) -> str:
return (
f"⚠️ Неизвестная команда.\n"
f"Пожалуйста, используйте цифры меню из сообщения выше.\n\n"
f"{emojis['9']} — Назад\n"
f"{emojis['0']} — В главное меню"
)
def _search_searching() -> str:
return "🔍 Ищу информацию в регламентах..."
def _search_not_found(emojis: dict) -> str:
return (
f"🤖 Точной информации в регламентах не найдено.\n\n"
f"Для выхода отправьте {emojis['0']}."
)
def _search_feedback_thanks(emojis: dict) -> str:
return (
f"🙏 Спасибо за ваш отзыв! Мы передали его разработчикам.\n\n"
f"🔍 Можете сразу задать следующий вопрос по регламентам.\n\n"
f"Для выхода отправьте {emojis['0']}."
)
def _search_feedback_info(emojis: dict) -> str:
return (
f"⚠️ Информация передана.\n\n"
f"Если желаете, напишите прямо сейчас текстовым сообщением, что конкретно вам не понравилось в ответе.\n\n"
f"{emojis['1']} — Задать ещё вопрос\n"
f"{emojis['0']} — Выйти в главное меню"
)
# --- Public constants and functions ---
SEARCH_MAIN_MENU_TEXT = _search_welcome(EMOJI_DIGITS)
SEARCH_UNKNOWN_CMD_TEXT = _search_unknown(EMOJI_DIGITS)
def search_searching_text() -> str:
return _search_searching()
def search_not_found_text(emojis: dict) -> str:
return _search_not_found(emojis)
def search_feedback_thanks_text(emojis: dict) -> str:
return _search_feedback_thanks(emojis)
def search_feedback_info_text(emojis: dict) -> str:
return _search_feedback_info(emojis)
def search_unavailable_action(emojis: dict) -> str:
return system_error_text(emojis, show_back=False)
# =========================================================
# LK BOT
# =========================================================
# Модуль Личного Кабинета (финансовые документы, табель и др.)
DOC_TYPES = {
"1": "Справка о доходах физического лица (2-НДФЛ)",
"2": "Справка об удержаниях за ДМС",
"3": "Справка о средней заработной плате",
"4": "Справка о получении единовременного пособия по рождению ребенка",
"5": "Справка о получении ежемесячного пособия и назначении отпуска по уходу за ребенком",
"6": "Справка об удержаниях по исполнительным листам",
"7": "Копия трудовой книжки"
}
# --- Private functions (Internal logic) ---
def _lk_auth_processing(emojis: dict) -> str:
return "⏳ Проверка безопасности... Запрашиваю контакты авторизации..."
def _lk_auth_success(emojis: dict) -> str:
return "⏳ Авторизация успешна. Загружаю детализацию расчетного листа..."
def _lk_auth_2fa_expired(emojis: dict) -> str:
return f"⏱ Время действия кода подтверждения изменилось или истекло.\nПожалуйста, запросите расчетный лист заново.{emojis['9']} — Назад"
def _lk_auth_2fa_wrong_code(emojis: dict, attempts_left: int) -> str:
return (
f"⚠️ Неверный код подтверждения.\nОсталось попыток: {attempts_left}. Попробуйте еще раз или вернитесь назад.\n\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
)
def _lk_auth_2fa_blocked_max(emojis: dict, minutes: int) -> str:
return (
f"❌ Превышено максимальное количество попыток ввода кода.\nДоступ к финансовым документам заблокирован на {minutes} минут.
\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
)
def _lk_payslip_loading(emojis: dict) -> str:
return "⏳ Формирую архив расчетных листков..."
def _lk_vacation_loading(emojis: dict) -> str:
return "⏳ Формирую справку по отпускам..."
def _lk_sick_loading(emojis: dict) -> str:
return "⏳ Загружаю историю больничных листов..."
def _lk_missions_loading(emojis: dict) -> str:
return "⏳ Загружаю историю командировок..."
def _lk_rewards_loading(emojis: dict) -> str:
return "⏳ Ищу информацию о ваших наградах..."
def _lk_transfers_loading(emojis: dict) -> str:
return "⏳ Загружаю историю кадровых перемещений..."
def _lk_tabel_loading(emojis: dict) -> str:
return "⏳ Рассчитываю баланс рабочего времени..."
def _lk_auth_sending(emojis: dict) -> str:
return "⏳ Отправка заявки..."
def _lk_success_submission(emojis: dict) -> str:
return (
"✅ Ваша заявка успешно отправлена!\n"
"Специалисты свяжутся с вами по готовности документа.\n\n"
f"{emojis['9']} — Назад в список справок\n{emojis['0']} — В главное меню"
)
def _lk_main_menu_text(header: str, emojis: dict) -> str:
return (
f"{header}\n"
f"{emojis['1']} — Расчётный лист\n"
f"{emojis['2']} — Информация о днях отпуска\n"
f"{emojis['3']} — Больничные\n"
f"{emojis['4']} — Командировки\n"
f"{emojis['5']} — Награды\n"
f"{emojis['6']} — Переводы\n"
f"{emojis['7']} — Табель учёта рабочего времени\n"
f"{emojis['8']} — Справки\n\n"
f"{emojis['0']} — Вернуться в главное меню"
)
def _lk_tabel_months_menu_text(prev_1, prev_2, months_ru, emojis: dict) -> str:
return (
"⏱ Выберите месяц для просмотра табеля:\n\n"
f"{emojis['1']} — {months_ru[prev_1.month]} {prev_1.year} г.\n"
f"{emojis['2']} — {months_ru[prev_2.month]} {prev_2.year} г.\n\n"
f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
)
def _lk_payslip_months_menu_text(months_data: list, months_ru: dict, emojis: dict) -> str:
menu_text = "📅 Выберите месяц для просмотра:\n\n"
for idx, cmonth in enumerate(months_data, start=1):
month_name = months_ru.get((cmonth - 1) % 12 + 1, '00')
year = (cmonth - 1) // 12
menu_text += f"{emojis[str(idx)]} — {month_name} {year} г.\n"
menu_text += f"\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
return menu_text
def _lk_docs_quantity_prompt(emojis: dict) -> str:
return f"Укажите необходимое количество экземпляров (максимум — 5 шт.):\n\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
def _lk_docs_year_prompt(emojis: dict) -> str:
return f"📅 Выберите год, за который требуется справка:\n\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
def _lk_years_menu(current_year: int, emojis: dict) -> str:
lines = ["📅 Выберите год запрашиваемого периода:\n"]
for i in range(1, 9):
year = current_year - (i - 1)
emoji = emojis.get(str(i), f"{i}⃣")
lines.append(f"{emoji} — {year} год")
lines.append(f"\n{emojis.get('9', '9⃣')} — Назад\n{emojis.get('0', '0⃣')} — В главное меню")
return "\n".join(lines)
def _lk_docs_avg_salary_prompt(emojis: dict) -> str:
return f"Укажите количество месяцев для расчета средней ЗП (например: 3):\n\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
def _lk_docs_child_age_prompt(emojis: dict) -> str:
return f"👶 Выберите возраст ребенка (до скольки лет):\n\n{emojis['1']} — до 1.5 лет\n{emojis['2']} — до 3 лет\n\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
def _lk_docs_period_prompt(year: int, emojis: dict) -> str:
return f"за {year} год\n\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
def _lk_docs_child_age_options(emojis: dict) -> str:
return f"{emojis['1']} — до 1.5 лет\n{emojis['2']} — до 3 лет\n\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
def _lk_docs_period_options(emojis: dict) -> str:
return f"{emojis['1']} — до 1.5 лет\n{emojis['2']} — до 3 лет\n\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
def _lk_nav_text(emojis: dict) -> str:
return f"\n\n{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
# --- Public constants and functions ---
LK_DOCS_MENU_TEXT = (
"\n".join([f"{EMOJI_DIGITS[k]} — {v}" for k, v in DOC_TYPES.items()]) +
f"\n\n{EMOJI_DIGITS['9']} — Назад\n{EMOJI_DIGITS['0']} — В главное меню"
)
LK_UNKNOWN_CMD_TEXT = (
"⚠️ Неизвестная команда.\n"
"Пожалуйста, выберите пункт меню, используя цифры или кнопки.
\n"
f"{EMOJI_DIGITS['9']} — Назад\n{EMOJI_DIGITS['0']} — В главное меню"
)
def lk_error_handling(emojis: dict) -> str:
return system_error_text(emojis, show_back=True)
def lk_auth_processing(emojis: dict) -> str:
return _lk_auth_processing(emojis)
def lk_auth_success(emojis: dict) -> str:
return _lk_auth_success(emojis)
def lk_auth_2fa_expired(emojis: dict) -> str:
return _lk_auth_2fa_expired(emojis)
def lk_auth_2fa_wrong_code(emojis: dict, attempts_left: int) -> str:
return _lk_auth_2fa_wrong_code(emojis, attempts_left)
def lk_auth_2fa_blocked_max(emojis: dict, minutes: int) -> str:
return _lk_auth_2fa_blocked_max(emojis, minutes)
def lk_payslip_loading(emojis: dict) -> str:
return _lk_payslip_loading(emojis)
def lk_vacation_loading(emojis: dict) -> str:
return _lk_vacation_loading(emojis)
def lk_sick_loading(emojis: dict) -> str:
return _lk_sick_loading(emojis)
def lk_missions_loading(emojis: dict) -> str:
return _lk_missions_loading(emojis)
def lk_rewards_loading(emojis: dict) -> str:
return _lk_rewards_loading(emojis)
def lk_transfers_loading(emojis: dict) -> str:
return _lk_transfers_loading(emojis)
def lk_tabel_loading(emojis: dict) -> str:
return _lk_tabel_loading(emojis)
def lk_auth_sending(emojis: dict) -> str:
return _lk_auth_sending(emojis)
def lk_success_submission(emojis: dict) -> str:
return _lk_success_submission(emojis)
def lk_main_menu_text(header: str, emojis: dict) -> str:
return _lk_main_menu_text(header, emojis)
def lk_tabel_months_menu_text(prev_1, prev_2, months_ru, emojis: dict) -> str:
return _lk_tabel_months_menu_text(prev_1, prev_2, months_ru, emojis)
def lk_payslip_months_menu_text(months_data: list, months_ru: dict, emojis: dict) -> str:
return _lk_payslip_months_menu_text(months_data, months_ru, emojis)
def lk_docs_quantity_prompt(emojis: dict) -> str:
return _lk_docs_quantity_prompt(emojis)
def lk_docs_year_prompt(emojis: dict) -> str:
return _lk_docs_year_prompt(emojis)
def lk_docs_avg_salary_prompt(emojis: dict) -> str:
return _lk_docs_avg_salary_prompt(emojis)
def lk_docs_child_age_prompt(emojis: dict) -> str:
return _lk_docs_child_age_prompt(emojis)
def lk_docs_period_prompt(year: int, emojis: dict) -> str:
return _lk_docs_period_prompt(year, emojis)
def lk_docs_child_age_options(emojis: dict) -> str:
return _lk_docs_child_age_options(emojis)
def lk_docs_period_options(emojis: dict) -> str:
return _lk_docs_period_options(emojis)
def lk_nav_text(emojis: dict) -> str:
return _lk_nav_text(emojis)
def lk_years_menu(current_year: int, emojis: dict) -> str:
return _lk_years_menu(current_year, emojis)
# =========================================================
# INSTRUCT BOT (Инструкции)
# =========================================================
# Модуль инструкций
# --- Private functions (Internal logic) ---
def _instruct_main_menu(emojis: dict) -> str:
return (
f"📋 Выберите инструкцию:\n\n"
f"{emojis['1']} — 🖥 Настройка TrueConf на мобильном устройстве\n"
f"{emojis['2']} — 📧 Настройка почты на мобильном устройстве\n\n"
f"{emojis['0']} — В главное меню"
)
def _instruct_trueconf(emojis: dict) -> str:
"""Статический текст Trueconf — без проверки группы."""
return (
"🖥 Инструкция по TrueConf\n\n"
"Данная функция находится в разработке.\n\n"
f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
)
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"{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
)
def _instruct_trueconf_no_ticket(emojis: dict, cn: str) -> str:
"""Текст Trueconf, когда пользователь не найден в AD."""
return (
f"🖥 Инструкция по TrueConf\n\n"
f"Пользователь {cn} не найден в Active Directory.\n\n"
f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
)
def _instruct_trueconf_with_ad(emojis: dict, cn: str, ticket_id: str = None) -> str:
"""
Чистый шаблон сообщения Trueconf:
- Если ticket_id передан: показываем статус заявки в ServiceDesk.
- Если ticket_id is None: у пользователя есть доступ, выводим подпись к файлу.
"""
if ticket_id:
return (
f"⚠ У Вас отсутствует доступ к TrueConf с мобильных устройств.\n\n"
f"🎫 Заявка #{ticket_id} для настройки создана в ServiceDesk!\n\n"
f"После выполнения заявки воспользуйтесь приложенной к этому сообщению инструкцией.\n\n"
f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
)
else:
return (
f"🖥 Инструкция по настройке TrueConf на мобильном устройстве\n\n"
f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
)
def _instruct_email_available(emojis: dict, cn: str, mail: str) -> str:
"""Текст когда почта доступна."""
return (
f"📧 Инструкция по настройке Почты на мобильном устройстве\n\n"
f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
)
def _instruct_email_history_ask(emojis: dict, cn: str) -> str:
"""Вопрос о наличии ранее доступа к мобильной почте."""
return (
f"📧 Был ли у Вас ранее доступ к мобильной почте?\n\n"
f"Напишите нужную цифру в ответ:\n\n"
f"{emojis['1']} — да, был\n"
f"{emojis['2']} — нет, не было\n\n"
f"{emojis['0']} — В главное меню"
)
def _instruct_email_unavailable(emojis: dict, cn: str) -> str:
"""Текст когда почта недоступна (старый вариант для обратной совместимости)."""
return (
f"⚠️ У Вас отсутствует доступ к Почте\n\n"
"Создайте, пожалуйста, заявку в Directum RX на модификацию прав пользователя.\n\n"
f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
)
def _instruct_email_not_had(emojis: dict, cn: str) -> str:
"""Текст когда пользователь ответил 'не было доступа к мобильной почте'."""
return (
f"⚠️ У Вас отсутствует доступ к мобильной почте.\n\n"
"Создайте, пожалуйста, заявку в Directum RX на \"Удалённый доступ к ИС\".\n\n"
f"{emojis['9']} — Назад\n{emojis['0']} — В главное меню"
)
# --- Public constants and functions ---
INSTRUCT_MAIN_MENU_TEXT = _instruct_main_menu(EMOJI_DIGITS)
INSTRUCT_TRUECONF_TEXT = _instruct_trueconf(EMOJI_DIGITS)
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_NOT_HAD = _instruct_email_not_had
INSTRUCT_EMAIL_UNAVAILABLE = _instruct_email_unavailable
# Единая стандартная заглушка системной ошибки для модуля инструкций
INSTRUCT_ERROR_MSG = system_error_text(EMOJI_DIGITS, show_back=True)