Ребрендинг БАЗА: новое меню + воронка подбора, интенсив, оплата, карточка менеджеру
Меню Кибершколы БАЗА (4 кнопки): Подобрать тренировку / Летний интенсив CS2 / Посмотреть цены / Задать вопрос. Воронка «Подобрать тренировку» (FSM BAZA_*): для кого → возраст → игра → уровень → формат → групповые / индивидуальные / нужен совет. На каждом шаге — «Назад» и «В главное меню», ветки: младше 12 и «другая игра» уводят к менеджеру. Ветка «Летний интенсив CS2»: инфо → возраст → уровень → оплата. Запуск по кнопке и по кодовым словам (лето/интенсив), основные триггеры и приветствие — из config. Шаг оплаты: инлайн-кнопка «Перейти к оплате» (ссылки-плейсхолдеры из config.LINKS + UTM), «Проблема с оплатой» и «Задать вопрос менеджеру» уводят к оператору. Карточка менеджеру собирается из профиля воронки (пустые поля пропускаются) + метки; дублируется на почту. Данные не переспрашиваются, после передачи менеджеру бот не вмешивается (OPERATOR_CHAT). Тесты воронки — pytest (30 passed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -600,6 +600,35 @@ class VKBotApplication:
|
||||
# Маршрутизация по состояниям (FSM)
|
||||
if state == "FREE_CHAT":
|
||||
self._handle_free_chat(user_id, text_clean, text_lower)
|
||||
# --- Воронка Кибершколы БАЗА ---
|
||||
elif state == "BAZA_FORWHO":
|
||||
self._handle_baza_forwho(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_AGE":
|
||||
self._handle_baza_age(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_YOUNG":
|
||||
self._handle_baza_young(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_GAME":
|
||||
self._handle_baza_game(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_GAME_OTHER":
|
||||
self._handle_baza_game_other(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_LEVEL":
|
||||
self._handle_baza_level(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_FORMAT":
|
||||
self._handle_baza_format(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_GROUP":
|
||||
self._handle_baza_group(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_INDIVIDUAL":
|
||||
self._handle_baza_individual(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_HELP":
|
||||
self._handle_baza_help(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_PAY":
|
||||
self._handle_baza_pay(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_INT_INFO":
|
||||
self._handle_baza_int_info(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_INT_AGE":
|
||||
self._handle_baza_int_age(user_id, text_clean, text_lower)
|
||||
elif state == "BAZA_INT_LEVEL":
|
||||
self._handle_baza_int_level(user_id, text_clean, text_lower)
|
||||
elif state == "FORM_RETURNING":
|
||||
self._handle_form_returning(user_id, text_clean, text_lower, user_data)
|
||||
elif state == "FORM_RET_NAME":
|
||||
@@ -670,104 +699,72 @@ class VKBotApplication:
|
||||
# ------------------------------------------
|
||||
|
||||
def _handle_free_chat(self, user_id: int, text: str, text_lower: str):
|
||||
"""Логика Свободного Чата (Главное меню, FAQ, переключение на форму)."""
|
||||
"""Главное меню Кибершколы БАЗА: воронка подбора, интенсив, цены, вопрос."""
|
||||
|
||||
# 1. Запуск формы опроса
|
||||
if text_lower in ["заполнить заявку 📝", "заявка", "начать опрос", "опрос"]:
|
||||
user_data = db.get_user_data(user_id)
|
||||
# Если клиент уже оставлял данные — предлагаем подтвердить их, а не вводить заново
|
||||
if user_data.get("name") and user_data.get("phone"):
|
||||
db.update_user_state(user_id, "FORM_RETURNING")
|
||||
self._send_returning_prompt(user_id, user_data)
|
||||
else:
|
||||
db.update_user_state(user_id, "FORM_NAME")
|
||||
self.send_message(
|
||||
user_id,
|
||||
"📝 Начинаем заполнение заявки!\n\n"
|
||||
f"{self._progress_bar(1)}\nКак к вам обращаться? Пожалуйста, введите ваше имя.",
|
||||
keyboard=get_cancel_keyboard()
|
||||
)
|
||||
# 1. Подобрать тренировку — запуск воронки
|
||||
if text_lower in ["подобрать тренировку 🎯", "подобрать тренировку", "подобрать заново 🎯",
|
||||
"подобрать", "подбор", "записаться", "тренировки", "занятия"]:
|
||||
self._baza_start_funnel(user_id)
|
||||
return
|
||||
|
||||
# 1.5 Площадки ИКП — кнопки-ссылки
|
||||
# 2. Летний интенсив CS2 (+ кодовые слова «лето», «интенсив» и т.п.)
|
||||
if text_lower in ["летний интенсив cs2 🔥", "летний интенсив cs2", "летний интенсив", "интенсив"] \
|
||||
or any(t in text_lower for t in config.SUMMER_TRIGGERS):
|
||||
self._baza_start_intensive(user_id)
|
||||
return
|
||||
|
||||
# 3. Посмотреть цены
|
||||
if text_lower in ["посмотреть цены 💰", "посмотреть цены", "цены", "цена", "стоимость", "прайс"]:
|
||||
self._baza_show_prices(user_id)
|
||||
return
|
||||
|
||||
# 3.1 Кнопки под ценами
|
||||
if text_lower in ["записаться на пробное", "пробное", "пробное занятие"]:
|
||||
self._baza_send_payment(user_id, "trial", "Пробное занятие")
|
||||
return
|
||||
if text_lower in ["выбрать абонемент", "абонемент", "абонементы"]:
|
||||
# Подбираем формат через воронку, затем предложим точный тариф
|
||||
self._baza_start_funnel(user_id)
|
||||
return
|
||||
|
||||
# 4. Задать вопрос / оператор / менеджер — передаём менеджеру
|
||||
if text_lower in ["задать вопрос 💬", "задать вопрос", "вопрос", "задать вопрос менеджеру 💬",
|
||||
"оператор", "менеджер", "человек", "позвать оператора", "связь с менеджером"]:
|
||||
self._baza_to_manager(user_id, reason="Прямой вопрос из меню")
|
||||
return
|
||||
|
||||
# 5. Площадки — кнопки-ссылки
|
||||
if text_lower in ["наши соцсети 🔗", "наши соцсети", "соцсети", "площадки", "ссылки"]:
|
||||
self.send_message(
|
||||
user_id,
|
||||
"🔗 Мы на площадках ИКП — заходите, подписывайтесь и смотрите трансляции 👇",
|
||||
"🔗 Мы на площадках Кибершколы БАЗА — заходите и подписывайтесь 👇",
|
||||
keyboard=get_socials_keyboard()
|
||||
)
|
||||
return
|
||||
|
||||
# 2. Переход к списку частых вопросов (FAQ)
|
||||
if text_lower in ["частые вопросы ❓", "faq", "вопросы", "помощь"]:
|
||||
# 6. Возврат в главное меню
|
||||
if text_lower in ["назад в меню ↩️", "меню", "назад", "в главное меню ↩️", "главное меню"]:
|
||||
self.send_message(
|
||||
user_id,
|
||||
"❓ Выберите тему, которая вас интересует, на клавиатуре ниже:",
|
||||
keyboard=get_faq_keyboard()
|
||||
)
|
||||
# Дополнительно — быстрые ответы из админки инлайн-кнопками
|
||||
custom = db.get_custom_faq()
|
||||
if custom:
|
||||
kb = VkKeyboard(inline=True)
|
||||
for n, (_id, kw, _ans) in enumerate(custom[:6]):
|
||||
if n and n % 2 == 0:
|
||||
kb.add_line()
|
||||
kb.add_button(kw[:40], color=VkKeyboardColor.SECONDARY)
|
||||
self.send_message(
|
||||
user_id,
|
||||
"💬 Популярные вопросы — нажмите, чтобы получить ответ:",
|
||||
keyboard=kb.get_keyboard()
|
||||
)
|
||||
return
|
||||
|
||||
# 3. Возврат в главное меню
|
||||
if text_lower in ["назад в меню ↩️", "меню", "назад", "в главное меню ↩️"]:
|
||||
self.send_message(
|
||||
user_id,
|
||||
"🏠 Вы вернулись в главное меню. Выберите интересующее действие:",
|
||||
"🏠 Главное меню Кибершколы БАЗА. Чем помочь?",
|
||||
keyboard=get_main_keyboard(is_admin=self._is_admin(user_id))
|
||||
)
|
||||
return
|
||||
|
||||
# 3.5 Раздел "Карьера в ИКП" (набор игроков и сотрудников)
|
||||
if text_lower in ["карьера в икп 💼", "карьера", "вакансии", "вакансия", "работа", "набор", "хочу в команду"]:
|
||||
db.clear_vac_data(user_id)
|
||||
db.update_user_state(user_id, "VAC_CHOOSE")
|
||||
self._send_career_choice(user_id)
|
||||
# 7. Приветствие (+ основные триггеры запуска)
|
||||
if text_lower in ["начать", "привет", "здравствуйте", "ку", "hi", "hello", "старт"] \
|
||||
or any(t in text_lower for t in config.MAIN_TRIGGERS):
|
||||
self._baza_send_greeting(user_id)
|
||||
return
|
||||
|
||||
# 4. Вызов оператора
|
||||
if text_lower in ["позвать оператора 🧑💼", "позвать оператора", "оператор", "менеджер", "человек", "позови оператора", "связь с оператором"]:
|
||||
self._handoff_to_operator(user_id)
|
||||
return
|
||||
|
||||
# 5. Обработка конкретных ответов FAQ
|
||||
for key, answer in config.FAQ_ANSWERS.items():
|
||||
if key in text_lower:
|
||||
self.send_message(user_id, answer, keyboard=get_faq_keyboard())
|
||||
return
|
||||
|
||||
# 6. Приветственные сообщения
|
||||
if text_lower in ["начать", "привет", "здравствуйте", "ку", "hi", "hello"]:
|
||||
self.send_message(
|
||||
user_id,
|
||||
"👋 Здравствуйте! Это чат-бот киберспортивной организации ИКП — Игра. Киберспорт. Профессионалы.\n\n"
|
||||
"Я помогу быстро оформить заявку на тренировки, рассказать об услугах или связать вас с командой. "
|
||||
"Также у нас открыт набор игроков и сотрудников — загляните в раздел «Карьера в ИКП 💼».\n\n"
|
||||
"Выберите действие на кнопках ниже 👇",
|
||||
keyboard=get_main_keyboard(is_admin=self._is_admin(user_id)),
|
||||
attachment=self.get_logo_attachment(user_id)
|
||||
)
|
||||
return
|
||||
|
||||
# 6.5 Быстрые ответы по ключевым словам (редактируются админом)
|
||||
# 8. Быстрые ответы по ключевым словам (редактируются админом)
|
||||
quick = db.match_custom_faq(text_lower)
|
||||
if quick:
|
||||
self.send_message(user_id, quick, keyboard=get_main_keyboard(is_admin=self._is_admin(user_id)))
|
||||
return
|
||||
|
||||
# 6.7 Автору поесть (запрет на удаление)
|
||||
if text_lower in ["/favi","/Favi", "/FAVI" ]:
|
||||
# 9. Пасхалка автора (запрет на удаление)
|
||||
if text_lower in ["/favi", "/Favi", "/FAVI"]:
|
||||
self.send_message(
|
||||
user_id,
|
||||
"Пасихалка для знающих\n\n"
|
||||
@@ -777,14 +774,421 @@ class VKBotApplication:
|
||||
keyboard=get_main_keyboard(is_admin=self._is_admin(user_id)),
|
||||
)
|
||||
return
|
||||
# 7. Fallback (Бот не понял, но пытается помочь, предлагая клавиатуру)
|
||||
|
||||
# 10. Fallback — не поняли запрос
|
||||
self.send_message(
|
||||
user_id,
|
||||
"🤔 Я пока только учусь и не смог полностью распознать ваш запрос.\n\n"
|
||||
"Пожалуйста, воспользуйтесь кнопками меню ниже или напишите 'оператор', чтобы я пригласил специалиста.",
|
||||
"🤔 Не совсем понял запрос. Давайте я помогу подобрать тренировку или отвечу на вопрос — "
|
||||
"выберите на кнопках ниже 👇",
|
||||
keyboard=get_main_keyboard(is_admin=self._is_admin(user_id))
|
||||
)
|
||||
|
||||
# ==========================================================
|
||||
# ВОРОНКА КИБЕРШКОЛЫ БАЗА (подбор тренировки, интенсив, оплата)
|
||||
# ==========================================================
|
||||
|
||||
def _utm(self, url: str, campaign: str = "main") -> str:
|
||||
"""Добавляет UTM-метку к ссылке (main — обычные, summer — интенсив)."""
|
||||
utm = config.UTM_SUMMER if campaign == "summer" else config.UTM_MAIN
|
||||
sep = "&" if "?" in url else "?"
|
||||
return f"{url}{sep}{utm}"
|
||||
|
||||
def _baza_send_greeting(self, user_id: int):
|
||||
db.add_metka(user_id, "bot_started")
|
||||
self.send_message(
|
||||
user_id,
|
||||
config.GREETING_BAZA + "\n\nВыберите действие на кнопках ниже 👇",
|
||||
keyboard=get_main_keyboard(is_admin=self._is_admin(user_id)),
|
||||
attachment=self.get_logo_attachment(user_id)
|
||||
)
|
||||
|
||||
def _baza_nav(self, user_id: int, text_lower: str) -> bool:
|
||||
"""Общие кнопки внутри воронки: меню / вопрос менеджеру. True — если обработали."""
|
||||
if text_lower in ["в главное меню ↩️", "главное меню", "меню", "в меню", "выход"]:
|
||||
db.update_user_state(user_id, "FREE_CHAT")
|
||||
self.send_message(
|
||||
user_id, "🏠 Главное меню Кибершколы БАЗА. Чем помочь?",
|
||||
keyboard=get_main_keyboard(is_admin=self._is_admin(user_id))
|
||||
)
|
||||
return True
|
||||
if text_lower in ["задать вопрос менеджеру 💬", "задать вопрос менеджеру", "вопрос менеджеру",
|
||||
"менеджер", "оператор"]:
|
||||
self._baza_to_manager(user_id, reason="Вопрос из подбора тренировки")
|
||||
return True
|
||||
return False
|
||||
|
||||
def _baza_start_funnel(self, user_id: int):
|
||||
"""Старт воронки «Подобрать тренировку»."""
|
||||
db.clear_profile(user_id)
|
||||
db.set_profile_field(user_id, "source", "Подбор тренировки")
|
||||
db.add_metka(user_id, "bot_started")
|
||||
db.add_metka(user_id, "funnel_started")
|
||||
db.update_user_state(user_id, "BAZA_FORWHO")
|
||||
self.send_message(
|
||||
user_id,
|
||||
"Отлично! Задам пару коротких вопросов и подберу подходящий формат 🎯\n\n"
|
||||
"Для кого подбираем тренировки?",
|
||||
keyboard=get_baza_forwho_keyboard()
|
||||
)
|
||||
|
||||
def _handle_baza_forwho(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if text_lower in ["для себя", "себе", "для меня"]:
|
||||
db.set_profile_field(user_id, "role", "Для себя (занимается сам)")
|
||||
db.add_metka(user_id, "role_self")
|
||||
elif text_lower in ["для подростка", "для ребёнка", "для ребенка", "для сына", "для дочери", "для подростка "]:
|
||||
db.set_profile_field(user_id, "role", "Для подростка (пишет родитель)")
|
||||
db.add_metka(user_id, "role_parent")
|
||||
else:
|
||||
self.send_message(user_id, "Выберите вариант на кнопках ниже 👇", keyboard=get_baza_forwho_keyboard())
|
||||
return
|
||||
db.update_user_state(user_id, "BAZA_AGE")
|
||||
self.send_message(user_id, "Сколько лет будущему ученику?", keyboard=get_baza_age_keyboard())
|
||||
|
||||
def _handle_baza_age(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if text_lower.startswith("назад"):
|
||||
db.update_user_state(user_id, "BAZA_FORWHO")
|
||||
self.send_message(user_id, "Для кого подбираем тренировки?", keyboard=get_baza_forwho_keyboard())
|
||||
return
|
||||
if "младше 12" in text_lower or "меньше 12" in text_lower:
|
||||
db.set_profile_field(user_id, "age", "Младше 12 лет")
|
||||
db.add_metka(user_id, "age_under12")
|
||||
db.update_user_state(user_id, "BAZA_YOUNG")
|
||||
self.send_message(
|
||||
user_id,
|
||||
"Обычно мы занимаемся с ребятами от 12 лет. Если ученику чуть меньше — это не проблема, "
|
||||
"лучше подобрать формат вместе с менеджером.",
|
||||
keyboard=get_baza_younger12_keyboard()
|
||||
)
|
||||
return
|
||||
if "12" in text_lower or "13" in text_lower:
|
||||
age, metka = "12–13 лет", "age_12_13"
|
||||
elif "14" in text_lower or "15" in text_lower:
|
||||
age, metka = "14–15 лет", "age_14_15"
|
||||
elif "16" in text_lower:
|
||||
age, metka = "16 лет и старше", "age_16plus"
|
||||
else:
|
||||
self.send_message(user_id, "Выберите возраст на кнопках ниже 👇", keyboard=get_baza_age_keyboard())
|
||||
return
|
||||
db.set_profile_field(user_id, "age", age)
|
||||
db.add_metka(user_id, metka)
|
||||
db.update_user_state(user_id, "BAZA_GAME")
|
||||
self.send_message(user_id, "Какая игра интересует?", keyboard=get_baza_game_keyboard())
|
||||
|
||||
def _handle_baza_young(self, user_id: int, text: str, text_lower: str):
|
||||
if text_lower.startswith("обсудить"):
|
||||
self._baza_to_manager(user_id, reason="Ученик младше 12 лет")
|
||||
return
|
||||
if text_lower.startswith("назад") or text_lower.startswith("вернуться"):
|
||||
db.update_user_state(user_id, "BAZA_AGE")
|
||||
self.send_message(user_id, "Сколько лет будущему ученику?", keyboard=get_baza_age_keyboard())
|
||||
return
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
self.send_message(user_id, "Выберите вариант на кнопках ниже 👇", keyboard=get_baza_younger12_keyboard())
|
||||
|
||||
def _handle_baza_game(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if text_lower.startswith("назад"):
|
||||
db.update_user_state(user_id, "BAZA_AGE")
|
||||
self.send_message(user_id, "Сколько лет будущему ученику?", keyboard=get_baza_age_keyboard())
|
||||
return
|
||||
if "cs" in text_lower or "кс" in text_lower or "контр" in text_lower:
|
||||
game, metka = "CS2", "game_cs2"
|
||||
elif "dota" in text_lower or "дота" in text_lower:
|
||||
game, metka = "Dota 2", "game_dota"
|
||||
elif "tank" in text_lower or "танк" in text_lower or "wot" in text_lower:
|
||||
game, metka = "World of Tanks", "game_wot"
|
||||
elif "друг" in text_lower:
|
||||
db.update_user_state(user_id, "BAZA_GAME_OTHER")
|
||||
self.send_message(
|
||||
user_id, "Напишите, какая игра интересует — передам менеджеру, он подскажет по возможностям.",
|
||||
keyboard=get_baza_game_keyboard()
|
||||
)
|
||||
return
|
||||
else:
|
||||
self.send_message(user_id, "Выберите игру на кнопках ниже 👇", keyboard=get_baza_game_keyboard())
|
||||
return
|
||||
db.set_profile_field(user_id, "game", game)
|
||||
db.add_metka(user_id, metka)
|
||||
db.update_user_state(user_id, "BAZA_LEVEL")
|
||||
self.send_message(user_id, "Какой сейчас уровень игры у ученика?", keyboard=get_baza_level_keyboard())
|
||||
|
||||
def _handle_baza_game_other(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if text_lower.startswith("назад"):
|
||||
db.update_user_state(user_id, "BAZA_GAME")
|
||||
self.send_message(user_id, "Какая игра интересует?", keyboard=get_baza_game_keyboard())
|
||||
return
|
||||
db.set_profile_field(user_id, "game", f"Другая игра: {text.strip()}")
|
||||
db.add_metka(user_id, "game_other")
|
||||
self._baza_to_manager(user_id, reason="Интересует другая игра")
|
||||
|
||||
def _handle_baza_level(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if text_lower.startswith("назад"):
|
||||
db.update_user_state(user_id, "BAZA_GAME")
|
||||
self.send_message(user_id, "Какая игра интересует?", keyboard=get_baza_game_keyboard())
|
||||
return
|
||||
levels = {
|
||||
"только начинает": ("Только начинает", "level_novice"),
|
||||
"играет для себя": ("Играет для себя", "level_casual"),
|
||||
"есть рейтинг или ранг": ("Есть рейтинг/ранг", "level_ranked"),
|
||||
"играет в команде или турнирах": ("Команда/турниры", "level_pro"),
|
||||
}
|
||||
match = levels.get(text_lower)
|
||||
if not match:
|
||||
self.send_message(user_id, "Выберите уровень на кнопках ниже 👇", keyboard=get_baza_level_keyboard())
|
||||
return
|
||||
db.set_profile_field(user_id, "level", match[0])
|
||||
db.add_metka(user_id, match[1])
|
||||
db.update_user_state(user_id, "BAZA_FORMAT")
|
||||
self.send_message(user_id, "Какой формат тренировок предпочтительнее?", keyboard=get_baza_format_keyboard())
|
||||
|
||||
def _handle_baza_format(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if text_lower.startswith("назад"):
|
||||
db.update_user_state(user_id, "BAZA_LEVEL")
|
||||
self.send_message(user_id, "Какой сейчас уровень игры у ученика?", keyboard=get_baza_level_keyboard())
|
||||
return
|
||||
if "групп" in text_lower:
|
||||
db.set_profile_field(user_id, "format", "Групповые тренировки")
|
||||
db.add_metka(user_id, "format_group")
|
||||
db.update_user_state(user_id, "BAZA_GROUP")
|
||||
self.send_message(
|
||||
user_id,
|
||||
"Групповые тренировки — занятия в мини-группе с тренером.\n\n"
|
||||
"🧑🤝🧑 Групповые:\n• 4 занятия — 2 790 ₽\n• 8 занятий — 4 990 ₽\n\n"
|
||||
"Можно начать с пробного занятия. Что выберете?",
|
||||
keyboard=get_baza_group_keyboard()
|
||||
)
|
||||
elif "индивид" in text_lower:
|
||||
db.set_profile_field(user_id, "format", "Индивидуальные тренировки")
|
||||
db.add_metka(user_id, "format_individual")
|
||||
db.update_user_state(user_id, "BAZA_INDIVIDUAL")
|
||||
self.send_message(
|
||||
user_id,
|
||||
"Индивидуальные тренировки — персональная работа с тренером один на один.\n\n"
|
||||
"👤 Индивидуальные:\n• 1 занятие — 870 ₽\n• 4 занятия — 3 290 ₽\n• 8 занятий — 5 990 ₽\n\n"
|
||||
"Можно начать с пробного занятия. Что выберете?",
|
||||
keyboard=get_baza_individual_keyboard()
|
||||
)
|
||||
elif "не знаю" in text_lower or "совет" in text_lower:
|
||||
db.set_profile_field(user_id, "format", "Нужен совет по формату")
|
||||
db.add_metka(user_id, "need_advice")
|
||||
db.update_user_state(user_id, "BAZA_HELP")
|
||||
self.send_message(
|
||||
user_id,
|
||||
"Без проблем — подскажем 🙌 Можно записаться на пробное занятие и определиться на практике "
|
||||
"или задать вопрос менеджеру, он поможет выбрать формат.",
|
||||
keyboard=get_baza_help_keyboard()
|
||||
)
|
||||
else:
|
||||
self.send_message(user_id, "Выберите формат на кнопках ниже 👇", keyboard=get_baza_format_keyboard())
|
||||
|
||||
def _handle_baza_group(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if text_lower.startswith("назад"):
|
||||
db.update_user_state(user_id, "BAZA_FORMAT")
|
||||
self.send_message(user_id, "Какой формат тренировок предпочтительнее?", keyboard=get_baza_format_keyboard())
|
||||
return
|
||||
if "пробное" in text_lower:
|
||||
self._baza_send_payment(user_id, "trial", "Пробное занятие (группа)")
|
||||
elif "4" in text_lower:
|
||||
self._baza_send_payment(user_id, "group_4", "Групповой абонемент, 4 занятия")
|
||||
elif "8" in text_lower:
|
||||
self._baza_send_payment(user_id, "group_8", "Групповой абонемент, 8 занятий")
|
||||
else:
|
||||
self.send_message(user_id, "Выберите вариант на кнопках ниже 👇", keyboard=get_baza_group_keyboard())
|
||||
|
||||
def _handle_baza_individual(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if text_lower.startswith("назад"):
|
||||
db.update_user_state(user_id, "BAZA_FORMAT")
|
||||
self.send_message(user_id, "Какой формат тренировок предпочтительнее?", keyboard=get_baza_format_keyboard())
|
||||
return
|
||||
if "пробное" in text_lower:
|
||||
self._baza_send_payment(user_id, "trial", "Пробное занятие (индивид.)")
|
||||
elif "1" in text_lower:
|
||||
self._baza_send_payment(user_id, "individual_1", "Индивидуальное, 1 занятие")
|
||||
elif "4" in text_lower:
|
||||
self._baza_send_payment(user_id, "individual_4", "Индивидуальные, 4 занятия")
|
||||
elif "8" in text_lower:
|
||||
self._baza_send_payment(user_id, "individual_8", "Индивидуальные, 8 занятий")
|
||||
else:
|
||||
self.send_message(user_id, "Выберите вариант на кнопках ниже 👇", keyboard=get_baza_individual_keyboard())
|
||||
|
||||
def _handle_baza_help(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if "пробное" in text_lower:
|
||||
self._baza_send_payment(user_id, "trial", "Пробное занятие")
|
||||
return
|
||||
if "подобрать" in text_lower:
|
||||
self._baza_start_funnel(user_id)
|
||||
return
|
||||
self.send_message(user_id, "Выберите вариант на кнопках ниже 👇", keyboard=get_baza_help_keyboard())
|
||||
|
||||
def _baza_send_payment(self, user_id: int, link_key: str, tariff_label: str, campaign: str = "main"):
|
||||
"""Показывает ссылку на оплату/запись + кнопки «проблема с оплатой» / «вопрос менеджеру»."""
|
||||
link = self._utm(config.LINKS.get(link_key, config.LINKS["tariffs"]), campaign)
|
||||
db.set_profile_field(user_id, "tariff", tariff_label)
|
||||
db.add_metka(user_id, "clicked_payment")
|
||||
db.add_metka(user_id, "lead_hot")
|
||||
db.update_user_state(user_id, "BAZA_PAY")
|
||||
kb = VkKeyboard(inline=True)
|
||||
kb.add_openlink_button("Перейти к оплате 💳", link)
|
||||
kb.add_line()
|
||||
kb.add_button("Проблема с оплатой", color=VkKeyboardColor.NEGATIVE)
|
||||
kb.add_line()
|
||||
kb.add_button("Задать вопрос менеджеру 💬", color=VkKeyboardColor.SECONDARY)
|
||||
self.send_message(
|
||||
user_id,
|
||||
f"Вы выбрали: {tariff_label}.\n\n"
|
||||
"Нажмите «Перейти к оплате 💳» — откроется страница оплаты. "
|
||||
"После оплаты менеджер свяжется с вами и подскажет время занятия.\n\n"
|
||||
"Если что-то не получается — нажмите «Проблема с оплатой», и мы поможем.",
|
||||
keyboard=kb.get_keyboard()
|
||||
)
|
||||
|
||||
def _handle_baza_pay(self, user_id: int, text: str, text_lower: str):
|
||||
if text_lower in ["проблема с оплатой", "проблема", "не получается", "ошибка оплаты"]:
|
||||
db.add_metka(user_id, "payment_problem")
|
||||
self._baza_to_manager(user_id, reason="Проблема с оплатой")
|
||||
return
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
# Любой другой текст — считаем вопросом и передаём менеджеру
|
||||
db.set_profile_field(user_id, "message", text.strip())
|
||||
self._baza_to_manager(user_id, reason="Вопрос на шаге оплаты")
|
||||
|
||||
# ---------- Летний интенсив CS2 ----------
|
||||
|
||||
def _baza_start_intensive(self, user_id: int):
|
||||
db.set_profile_field(user_id, "source", "Летний интенсив CS2")
|
||||
db.add_metka(user_id, "bot_started")
|
||||
db.add_metka(user_id, "intensive_interest")
|
||||
db.update_user_state(user_id, "BAZA_INT_INFO")
|
||||
self.send_message(
|
||||
user_id,
|
||||
config.INTENSIVE_INFO + "\n\nХотите записаться на интенсив?",
|
||||
keyboard=get_baza_intensive_keyboard()
|
||||
)
|
||||
|
||||
def _handle_baza_int_info(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if "участвовать" in text_lower or "записаться" in text_lower or "хочу" in text_lower or "да" == text_lower:
|
||||
db.update_user_state(user_id, "BAZA_INT_AGE")
|
||||
self.send_message(user_id, "Отлично! Сколько лет участнику?", keyboard=get_baza_age_keyboard())
|
||||
return
|
||||
self.send_message(user_id, "Выберите действие на кнопках ниже 👇", keyboard=get_baza_intensive_keyboard())
|
||||
|
||||
def _handle_baza_int_age(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if text_lower.startswith("назад"):
|
||||
db.update_user_state(user_id, "BAZA_INT_INFO")
|
||||
self.send_message(user_id, "Хотите записаться на интенсив?", keyboard=get_baza_intensive_keyboard())
|
||||
return
|
||||
if "младше 12" in text_lower or "меньше 12" in text_lower:
|
||||
db.set_profile_field(user_id, "age", "Младше 12 лет")
|
||||
db.add_metka(user_id, "age_under12")
|
||||
self._baza_to_manager(user_id, reason="Интенсив: участник младше 12 лет")
|
||||
return
|
||||
if "12" in text_lower or "13" in text_lower:
|
||||
age = "12–13 лет"
|
||||
elif "14" in text_lower or "15" in text_lower:
|
||||
age = "14–15 лет"
|
||||
elif "16" in text_lower:
|
||||
age = "16 лет и старше"
|
||||
else:
|
||||
self.send_message(user_id, "Выберите возраст на кнопках ниже 👇", keyboard=get_baza_age_keyboard())
|
||||
return
|
||||
db.set_profile_field(user_id, "age", age)
|
||||
db.update_user_state(user_id, "BAZA_INT_LEVEL")
|
||||
self.send_message(user_id, "Какой сейчас уровень игры в CS2?", keyboard=get_baza_level_keyboard())
|
||||
|
||||
def _handle_baza_int_level(self, user_id: int, text: str, text_lower: str):
|
||||
if self._baza_nav(user_id, text_lower):
|
||||
return
|
||||
if text_lower.startswith("назад"):
|
||||
db.update_user_state(user_id, "BAZA_INT_AGE")
|
||||
self.send_message(user_id, "Сколько лет участнику?", keyboard=get_baza_age_keyboard())
|
||||
return
|
||||
levels = {
|
||||
"только начинает": "Только начинает",
|
||||
"играет для себя": "Играет для себя",
|
||||
"есть рейтинг или ранг": "Есть рейтинг/ранг",
|
||||
"играет в команде или турнирах": "Команда/турниры",
|
||||
}
|
||||
match = levels.get(text_lower)
|
||||
if not match:
|
||||
self.send_message(user_id, "Выберите уровень на кнопках ниже 👇", keyboard=get_baza_level_keyboard())
|
||||
return
|
||||
db.set_profile_field(user_id, "level", match)
|
||||
db.set_profile_field(user_id, "game", "CS2 (интенсив)")
|
||||
self._baza_send_payment(user_id, "summer", f"Летний интенсив CS2 — {config.INTENSIVE_PRICE}", campaign="summer")
|
||||
|
||||
# ---------- Цены / передача менеджеру / карточка ----------
|
||||
|
||||
def _baza_show_prices(self, user_id: int):
|
||||
db.update_user_state(user_id, "FREE_CHAT")
|
||||
self.send_message(user_id, config.PRICES_TEXT + "\n\nЧто дальше?", keyboard=get_baza_prices_keyboard())
|
||||
|
||||
def _baza_manager_card(self, user_id: int, reason: str = "") -> str:
|
||||
p = db.get_profile(user_id)
|
||||
vk_name = self._vk_name(user_id) or "—"
|
||||
lines = ["🎯 Новый лид — Кибершкола БАЗА", ""]
|
||||
if reason:
|
||||
lines.append(f"❗ Повод: {reason}")
|
||||
lines.append(f"👤 Клиент: {vk_name}")
|
||||
lines.append(f"🔗 Профиль: vk.com/id{user_id}")
|
||||
lines.append("")
|
||||
fields = [
|
||||
("source", "Источник"), ("role", "Для кого"), ("age", "Возраст"),
|
||||
("game", "Игра"), ("level", "Уровень"), ("format", "Формат"),
|
||||
("tariff", "Интерес / тариф"), ("message", "Сообщение"),
|
||||
]
|
||||
for key, label in fields:
|
||||
val = p.get(key)
|
||||
if val:
|
||||
lines.append(f"• {label}: {val}")
|
||||
metki = p.get("metki")
|
||||
if metki:
|
||||
lines.append("")
|
||||
lines.append("🏷 Метки: " + ", ".join(metki))
|
||||
return "\n".join(lines)
|
||||
|
||||
def _baza_to_manager(self, user_id: int, reason: str = ""):
|
||||
"""Передаёт лид менеджеру с карточкой профиля и переводит в чат с оператором."""
|
||||
db.add_metka(user_id, "lead_manager")
|
||||
card = self._baza_manager_card(user_id, reason)
|
||||
self._notify_operators("💬 Запрос менеджеру\n\n" + card)
|
||||
try:
|
||||
mailer.send_email(f"Лид из бота БАЗА — vk.com/id{user_id}", card)
|
||||
except Exception as mail_err:
|
||||
logger.error(f"Не удалось отправить email о лиде: {mail_err}")
|
||||
db.update_user_state(user_id, "OPERATOR_CHAT")
|
||||
if self._within_work_hours():
|
||||
client_text = ("💬 Передал вас менеджеру — он ответит здесь в ближайшее время.\n"
|
||||
"Можете написать свой вопрос прямо сейчас.")
|
||||
else:
|
||||
client_text = (f"🌙 Менеджер сейчас оффлайн (работаем с {getattr(config, 'WORK_HOUR_START', 9)}:00 "
|
||||
f"до {getattr(config, 'WORK_HOUR_END', 21)}:00).\n"
|
||||
"Я передал ваш запрос — ответим, как начнётся рабочий день. "
|
||||
"Можете оставить сообщение здесь.")
|
||||
self.send_message(user_id, client_text, keyboard=get_operator_keyboard())
|
||||
|
||||
|
||||
def _send_returning_prompt(self, user_id: int, user_data: Dict[str, Any]):
|
||||
"""Приветствие для клиента, чьи имя и телефон уже сохранены."""
|
||||
|
||||
+95
-9
@@ -8,24 +8,110 @@ import config
|
||||
# ==========================================
|
||||
|
||||
def get_main_keyboard(is_admin: bool = False) -> str:
|
||||
"""Главное меню бота. Для админа добавляется кнопка «Админка ⚙️»."""
|
||||
"""Главное меню Кибершколы БАЗА. Для админа добавляется кнопка «Админка ⚙️»."""
|
||||
keyboard = VkKeyboard(one_time=False)
|
||||
# Кнопка начать опрос (зеленая)
|
||||
keyboard.add_button("Заполнить заявку 📝", color=VkKeyboardColor.PRIMARY)
|
||||
keyboard.add_button("Подобрать тренировку 🎯", color=VkKeyboardColor.PRIMARY)
|
||||
keyboard.add_line()
|
||||
# Карьера в ИКП (набор игроков и сотрудников)
|
||||
keyboard.add_button("Карьера в ИКП 💼", color=VkKeyboardColor.SECONDARY)
|
||||
keyboard.add_button("Летний интенсив CS2 🔥", color=VkKeyboardColor.SECONDARY)
|
||||
keyboard.add_line()
|
||||
# Кнопки FAQ и оператора
|
||||
keyboard.add_button("Частые вопросы ❓", color=VkKeyboardColor.SECONDARY)
|
||||
keyboard.add_button("Позвать оператора 🧑💼", color=VkKeyboardColor.NEGATIVE)
|
||||
# Кнопка админ-панели — только для администраторов (отдельной строкой)
|
||||
keyboard.add_button("Посмотреть цены 💰", color=VkKeyboardColor.SECONDARY)
|
||||
keyboard.add_button("Задать вопрос 💬", color=VkKeyboardColor.SECONDARY)
|
||||
if is_admin:
|
||||
keyboard.add_line()
|
||||
keyboard.add_button("Админка ⚙️", color=VkKeyboardColor.POSITIVE)
|
||||
return keyboard.get_keyboard()
|
||||
|
||||
|
||||
# ==========================================
|
||||
# КЛАВИАТУРЫ ВОРОНКИ «ПОДОБРАТЬ ТРЕНИРОВКУ» (БАЗА)
|
||||
# ==========================================
|
||||
|
||||
def _kb(rows, back=False, ask_manager=False, back_menu=True):
|
||||
"""Хелпер: строит нижнюю клавиатуру из строк кнопок + служебные кнопки снизу.
|
||||
|
||||
back — кнопка «Назад ⬅️» (к предыдущему вопросу воронки);
|
||||
ask_manager — кнопка «Задать вопрос менеджеру 💬»;
|
||||
back_menu — кнопка «В главное меню ↩️».
|
||||
"""
|
||||
keyboard = VkKeyboard(one_time=False)
|
||||
for i, row in enumerate(rows):
|
||||
if i:
|
||||
keyboard.add_line()
|
||||
for label in row:
|
||||
keyboard.add_button(label, color=VkKeyboardColor.SECONDARY)
|
||||
if ask_manager:
|
||||
keyboard.add_line()
|
||||
keyboard.add_button("Задать вопрос менеджеру 💬", color=VkKeyboardColor.SECONDARY)
|
||||
if back or back_menu:
|
||||
keyboard.add_line()
|
||||
if back:
|
||||
keyboard.add_button("Назад ⬅️", color=VkKeyboardColor.SECONDARY)
|
||||
if back_menu:
|
||||
keyboard.add_button("В главное меню ↩️", color=VkKeyboardColor.SECONDARY)
|
||||
return keyboard.get_keyboard()
|
||||
|
||||
|
||||
def get_baza_forwho_keyboard() -> str:
|
||||
return _kb([["Для себя", "Для подростка"]])
|
||||
|
||||
|
||||
def get_baza_age_keyboard() -> str:
|
||||
return _kb([["12–13 лет", "14–15 лет"], ["16 лет и старше", "Младше 12 лет"]], back=True)
|
||||
|
||||
|
||||
def get_baza_younger12_keyboard() -> str:
|
||||
keyboard = VkKeyboard(one_time=False)
|
||||
keyboard.add_button("Обсудить с менеджером 💬", color=VkKeyboardColor.SECONDARY)
|
||||
keyboard.add_line()
|
||||
keyboard.add_button("Назад ⬅️", color=VkKeyboardColor.SECONDARY)
|
||||
keyboard.add_button("В главное меню ↩️", color=VkKeyboardColor.SECONDARY)
|
||||
return keyboard.get_keyboard()
|
||||
|
||||
|
||||
def get_baza_game_keyboard() -> str:
|
||||
return _kb([["CS2", "Dota 2"], ["World of Tanks", "Другая игра"]], back=True)
|
||||
|
||||
|
||||
def get_baza_level_keyboard() -> str:
|
||||
return _kb([["Только начинает"], ["Играет для себя"],
|
||||
["Есть рейтинг или ранг"], ["Играет в команде или турнирах"]], back=True)
|
||||
|
||||
|
||||
def get_baza_format_keyboard() -> str:
|
||||
return _kb([["Групповые тренировки"], ["Индивидуальные тренировки"],
|
||||
["Пока не знаю, нужен совет"]], back=True)
|
||||
|
||||
|
||||
def get_baza_group_keyboard() -> str:
|
||||
return _kb([["Записаться на пробное"], ["Купить 4 тренировки", "Купить 8 тренировок"]],
|
||||
back=True, ask_manager=True)
|
||||
|
||||
|
||||
def get_baza_individual_keyboard() -> str:
|
||||
return _kb([["Записаться на пробное"], ["Купить 1 тренировку"],
|
||||
["Купить 4 тренировки", "Купить 8 тренировок"]],
|
||||
back=True, ask_manager=True)
|
||||
|
||||
|
||||
def get_baza_help_keyboard() -> str:
|
||||
return _kb([["Записаться на пробное"], ["Подобрать заново 🎯"]], ask_manager=True)
|
||||
|
||||
|
||||
def get_baza_intensive_keyboard() -> str:
|
||||
return _kb([["Хочу участвовать 🔥"]], ask_manager=True)
|
||||
|
||||
|
||||
def get_baza_prices_keyboard() -> str:
|
||||
keyboard = VkKeyboard(one_time=False)
|
||||
keyboard.add_button("Подобрать тренировку 🎯", color=VkKeyboardColor.PRIMARY)
|
||||
keyboard.add_line()
|
||||
keyboard.add_button("Записаться на пробное", color=VkKeyboardColor.SECONDARY)
|
||||
keyboard.add_button("Выбрать абонемент", color=VkKeyboardColor.SECONDARY)
|
||||
keyboard.add_line()
|
||||
keyboard.add_button("Задать вопрос 💬", color=VkKeyboardColor.SECONDARY)
|
||||
return keyboard.get_keyboard()
|
||||
|
||||
|
||||
def get_admin_keyboard(is_super: bool = False) -> str:
|
||||
"""Клавиатура админ-панели с разделами. Супер-админу — раздел «Администраторы»."""
|
||||
keyboard = VkKeyboard(one_time=False)
|
||||
|
||||
+74
-17
@@ -5,26 +5,83 @@ def _state(uid):
|
||||
return bot.db.get_user_data(uid)["state"]
|
||||
|
||||
|
||||
def test_full_lead_flow(app):
|
||||
def _prof(uid):
|
||||
return bot.db.get_profile(uid)
|
||||
|
||||
|
||||
def test_baza_group_funnel_to_payment(app):
|
||||
"""Полный подбор → групповой абонемент → шаг оплаты (BAZA_PAY)."""
|
||||
u = 70001
|
||||
assert (app.handle_message(u, "Заполнить заявку 📝"), _state(u))[1] == "FORM_NAME"
|
||||
app.handle_message(u, "Иван"); assert _state(u) == "FORM_PHONE"
|
||||
app.handle_message(u, "89990001122"); assert _state(u) == "FORM_QUESTION"
|
||||
app.handle_message(u, "кс"); assert _state(u) == "FORM_CALLDATE"
|
||||
app.handle_message(u, "Завтра"); assert _state(u) == "FORM_CALLTIME"
|
||||
app.handle_message(u, "14:00"); assert _state(u) == "FORM_REVIEW"
|
||||
app.handle_message(u, "Да, всё верно! ✅"); assert _state(u) == "FORM_CONSENT_1"
|
||||
app.handle_message(u, "Согласен(а) и подтверждаю ✅"); assert _state(u) == "FORM_CONSENT_2"
|
||||
app.handle_message(u, "Без рассылки ➡️"); assert _state(u) == "FREE_CHAT"
|
||||
stats = bot.db.get_lead_stats()
|
||||
assert stats["total"] == 1 and stats["from_bot"] == 1
|
||||
app.handle_message(u, "Подобрать тренировку 🎯"); assert _state(u) == "BAZA_FORWHO"
|
||||
app.handle_message(u, "Для подростка"); assert _state(u) == "BAZA_AGE"
|
||||
app.handle_message(u, "14–15 лет"); assert _state(u) == "BAZA_GAME"
|
||||
app.handle_message(u, "CS2"); assert _state(u) == "BAZA_LEVEL"
|
||||
app.handle_message(u, "Играет для себя"); assert _state(u) == "BAZA_FORMAT"
|
||||
app.handle_message(u, "Групповые тренировки"); assert _state(u) == "BAZA_GROUP"
|
||||
app.handle_message(u, "Купить 4 тренировки"); assert _state(u) == "BAZA_PAY"
|
||||
p = _prof(u)
|
||||
assert p["role"].startswith("Для подростка") and p["game"] == "CS2"
|
||||
assert p["format"] == "Групповые тренировки" and "4" in p["tariff"]
|
||||
assert {"role_parent", "game_cs2", "format_group", "clicked_payment", "lead_hot"} <= set(p["metki"])
|
||||
|
||||
|
||||
def test_career_player_flow(app):
|
||||
u = 70003
|
||||
app.handle_message(u, "Карьера в ИКП 💼"); assert _state(u) == "VAC_CHOOSE"
|
||||
app.handle_message(u, "Игроком в состав 🎮"); assert _state(u) == "VAC_P_DISCIPLINE"
|
||||
app.handle_message(u, "Counter-Strike 2 🔫"); assert _state(u) == "VAC_P_RANK"
|
||||
def test_baza_individual_and_back(app):
|
||||
"""Индивидуальная ветка + кнопка «Назад» возвращает на предыдущий шаг."""
|
||||
u = 70011
|
||||
app.handle_message(u, "Подобрать тренировку 🎯")
|
||||
app.handle_message(u, "Для себя"); assert _state(u) == "BAZA_AGE"
|
||||
app.handle_message(u, "16 лет и старше"); assert _state(u) == "BAZA_GAME"
|
||||
app.handle_message(u, "Назад ⬅️"); assert _state(u) == "BAZA_AGE"
|
||||
app.handle_message(u, "16 лет и старше")
|
||||
app.handle_message(u, "Dota 2"); assert _state(u) == "BAZA_LEVEL"
|
||||
app.handle_message(u, "Есть рейтинг или ранг")
|
||||
app.handle_message(u, "Индивидуальные тренировки"); assert _state(u) == "BAZA_INDIVIDUAL"
|
||||
app.handle_message(u, "Купить 8 тренировок"); assert _state(u) == "BAZA_PAY"
|
||||
assert "8" in _prof(u)["tariff"]
|
||||
|
||||
|
||||
def test_baza_under12_goes_to_manager(app):
|
||||
u = 70012
|
||||
app.handle_message(u, "Подобрать тренировку 🎯")
|
||||
app.handle_message(u, "Для подростка")
|
||||
app.handle_message(u, "Младше 12 лет"); assert _state(u) == "BAZA_YOUNG"
|
||||
app.handle_message(u, "Обсудить с менеджером 💬")
|
||||
assert _state(u) == "OPERATOR_CHAT"
|
||||
assert "age_under12" in _prof(u)["metki"]
|
||||
|
||||
|
||||
def test_baza_other_game_goes_to_manager(app):
|
||||
u = 70013
|
||||
app.handle_message(u, "Подобрать тренировку 🎯")
|
||||
app.handle_message(u, "Для себя")
|
||||
app.handle_message(u, "16 лет и старше")
|
||||
app.handle_message(u, "Другая игра"); assert _state(u) == "BAZA_GAME_OTHER"
|
||||
app.handle_message(u, "Valorant"); assert _state(u) == "OPERATOR_CHAT"
|
||||
assert "Valorant" in _prof(u)["game"]
|
||||
|
||||
|
||||
def test_baza_intensive_by_keyword(app):
|
||||
"""Кодовое слово «лето» запускает интенсив; ветка доходит до оплаты."""
|
||||
u = 70014
|
||||
app.handle_message(u, "лето"); assert _state(u) == "BAZA_INT_INFO"
|
||||
app.handle_message(u, "Хочу участвовать 🔥"); assert _state(u) == "BAZA_INT_AGE"
|
||||
app.handle_message(u, "14–15 лет"); assert _state(u) == "BAZA_INT_LEVEL"
|
||||
app.handle_message(u, "Играет в команде или турнирах"); assert _state(u) == "BAZA_PAY"
|
||||
assert "intensive_interest" in _prof(u)["metki"]
|
||||
|
||||
|
||||
def test_baza_question_button_handoff(app):
|
||||
u = 70015
|
||||
app.handle_message(u, "Задать вопрос 💬"); assert _state(u) == "OPERATOR_CHAT"
|
||||
assert "lead_manager" in _prof(u)["metki"]
|
||||
|
||||
|
||||
def test_baza_prices_and_menu(app):
|
||||
u = 70016
|
||||
app._sent.clear()
|
||||
app.handle_message(u, "Посмотреть цены 💰")
|
||||
assert _state(u) == "FREE_CHAT"
|
||||
assert any("2 790" in s["msg"] for s in app._sent)
|
||||
|
||||
|
||||
def test_flood_does_not_crash(app):
|
||||
|
||||
Reference in New Issue
Block a user