"""Пулы фраз, которыми бот обозначает ход долгой операции. Фразы показываются в живом драфте (``sendMessageDraft``) в приватном чате и сменяют друг друга случайным образом через случайные промежутки времени, пока операция не завершилась. Ни одна фраза не обещает конкретного результата — это только обозначение процесса. """ from __future__ import annotations import os import random GENERATION_PHRASES: tuple[str, ...] = ( "⏳ Разбираю запрос и подбираю формулировки…", "⏳ Собираю фактуру из того, что ты прислал…", "⏳ Придумываю три разных захода к теме…", "⏳ Проверяю, что не придумываю лишних фактов…", "⏳ Подбираю более точное начало поста…", "⏳ Слежу за длиной и читаемостью каждого варианта…", "⏳ Убираю канцелярит и лишние вводные…", "⏳ Собираю три варианта и проверяю орфографию…", ) TRAINING_PHRASES: tuple[str, ...] = ( "⏳ Читаю присланные материалы…", "⏳ Выделяю особенности твоей подачи…", "⏳ Сопоставляю новые примеры с прежним профилем…", "⏳ Уточняю портрет аудитории…", "⏳ Отмечаю типичные темы и повороты…", "⏳ Собираю обновлённый профиль аккаунта…", "⏳ Проверяю, что ничего не потерялось…", "⏳ Упаковываю вывод в компактный профиль…", ) IMAGES_PHRASES: tuple[str, ...] = ( "⏳ Скачиваю изображения…", "⏳ Распознаю тексты на скриншотах…", "⏳ Отделяю заголовки от основного текста…", "⏳ Сверяю извлечённые посты между собой…", "⏳ Ищу повторяющиеся приёмы в текстах…", "⏳ Проверяю, что тексты прочитаны полностью…", "⏳ Готовлю варианты поста по увиденному…", ) VOICE_PHRASES: tuple[str, ...] = ( "⏳ Скачиваю аудиосообщение…", "⏳ Перевожу речь в текст…", "⏳ Расставляю знаки препинания в расшифровке…", "⏳ Уточняю границы фраз…", "⏳ Проверяю расшифровку на слух…", "⏳ Разбираю, что именно ты просишь…", ) IDEAS_PHRASES: tuple[str, ...] = ( "⏳ Ищу свежий угол для темы…", "⏳ Проверяю, что идея не повторяет прошлые…", "⏳ Подбираю формулировку заголовка…", "⏳ Оцениваю пользу идеи для твоей аудитории…", "⏳ Формулирую, зачем эта тема аккаунту…", "⏳ Отбираю самый сильный вариант…", ) DEFAULT_PHRASES: tuple[str, ...] = ( "⏳ Работаю над запросом…", "⏳ Привожу результат к нужному виду…", "⏳ Почти готова, проверяю детали…", ) # Фразы, с которых живой оператор начинает диалог в поддержке. Бот не должен # рассказывать пользователю про «передачу оператору» — он просто здоровается # и приглашает описать проблему: для пользователя это начало разговора с человеком. # Формулировки безличные или без глаголов в первом лице, чтобы подходить # любому оператору и не спорить с голосом бота. SUPPORT_GREETINGS: tuple[str, ...] = ( "Здравствуйте! На связи поддержка. Расскажите, что случилось — разберёмся.", "Добрый день! Слушаю вас: опишите проблему, и постараемся помочь.", "Приветствую! Чем могу помочь? Опишите ситуацию подробнее.", "Здравствуйте! На связи. Что пошло не так — напишите, посмотрим.", "Добрый день! На связи поддержка. Опишите, с чем возникла сложность.", "Здравствуйте! Слушаю. Расскажите, что случилось, — постараемся решить.", "Приветствую! Готовы помочь. Опишите проблему, и разберёмся вместе.", "Добрый день! На связи. Напишите, что не работает, — посмотрим.", "Здравствуйте! Поддержка на связи. Опишите вопрос — поможем разобраться.", "Приветствую! На связи. С чего начнём — опишите, что случилось.", ) SUPPORT_GREETING_FALLBACK = "Здравствуйте! На связи поддержка. Расскажите, что случилось." ACTIVITY_PHRASES: dict[str, tuple[str, ...]] = { "generation": GENERATION_PHRASES, "training": TRAINING_PHRASES, "images": IMAGES_PHRASES, "voice": VOICE_PHRASES, "ideas": IDEAS_PHRASES, } def _positive_float(name: str, default: float) -> float: raw = os.getenv(name, "").strip() if not raw: return default try: value = float(raw) except ValueError: return default return value if value > 0 else default def _positive_int(name: str, default: int) -> int: raw = os.getenv(name, "").strip() if not raw: return default try: value = int(raw) except ValueError: return default return value if value > 0 else default ACTIVITY_MIN_SECONDS = _positive_float("ACTIVITY_MIN_SECONDS", 3.0) ACTIVITY_MAX_SECONDS = _positive_float("ACTIVITY_MAX_SECONDS", 8.0) if ACTIVITY_MIN_SECONDS > ACTIVITY_MAX_SECONDS: ACTIVITY_MIN_SECONDS, ACTIVITY_MAX_SECONDS = ACTIVITY_MAX_SECONDS, ACTIVITY_MIN_SECONDS # Предохранитель: если операция зависла и finish() не вызвался, ротация # всё равно прекратится и не будет бесконечно долбить драфт. ACTIVITY_MAX_ROTATIONS = _positive_int("ACTIVITY_MAX_ROTATIONS", 12) def random_interval() -> float: """Случайная пауза между сменами фраз, в секундах.""" return random.uniform(ACTIVITY_MIN_SECONDS, ACTIVITY_MAX_SECONDS) def next_phrase(kind: str | None = None, exclude: str | None = None) -> str: """Случайная фраза из пула операции, не повторяющая только что показанную.""" pool = ACTIVITY_PHRASES.get(kind or "", DEFAULT_PHRASES) if exclude is None or len(pool) < 2: return random.choice(pool) variants = tuple(phrase for phrase in pool if phrase != exclude) return random.choice(variants) def support_greeting() -> str: """Случайная фраза начала диалога поддержки. Пул задаётся переменной окружения SUPPORT_GREETINGS (разделитель — `|`), чтобы оператор мог поменять формулировки без деплоя. Пустые строки отбрасываются; если пул пуст, возвращается SUPPORT_GREETING_FALLBACK. """ raw = os.getenv("SUPPORT_GREETINGS", "").strip() if raw: pool = tuple(item.strip() for item in raw.split("|") if item.strip()) if pool: return random.choice(pool) return random.choice(SUPPORT_GREETINGS) if SUPPORT_GREETINGS else SUPPORT_GREETING_FALLBACK