Умный пропуск шагов воронки при уже сохранённых данных (§15)
- _baza_advance: воронка переходит к первому НЕзаполненному шагу, пропуская роль/ возраст/игру/уровень, если они уже известны; формат (выбор действия) спрашивается всегда. - На повторном входе — короткая подсказка, что известные ответы не переспрашиваются и их можно изменить кнопкой «Назад» (back-навигация остаётся линейной = override). - Интенсив: известный возраст (из подбора) не переспрашивается — сразу уровень CS2. - Данные из ветки «лето» сохраняются и переиспользуются в подборе, и наоборот. Тесты: test_baza_smart_skip_known_fields, test_baza_intensive_skips_known_age. pytest 33 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -695,17 +695,48 @@ class VKBotApplication:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Шаги воронки: порядок и какое поле профиля заполняет каждый (format — без поля,
|
||||||
|
# это выбор действия, поэтому спрашивается всегда).
|
||||||
|
_FUNNEL_ORDER = ["BAZA_FORWHO", "BAZA_AGE", "BAZA_GAME", "BAZA_LEVEL", "BAZA_FORMAT"]
|
||||||
|
_FUNNEL_FIELD = {"BAZA_FORWHO": "role", "BAZA_AGE": "age", "BAZA_GAME": "game", "BAZA_LEVEL": "level"}
|
||||||
|
|
||||||
|
def _baza_ask(self, user_id: int, state: str):
|
||||||
|
"""Задаёт вопрос конкретного шага воронки и ставит соответствующее состояние."""
|
||||||
|
prompts = {
|
||||||
|
"BAZA_FORWHO": ("Для кого подбираем тренировки?", get_baza_forwho_keyboard()),
|
||||||
|
"BAZA_AGE": ("Сколько лет игроку?", get_baza_age_keyboard()),
|
||||||
|
"BAZA_GAME": ("По какой игре хотите заниматься?", get_baza_game_keyboard()),
|
||||||
|
"BAZA_LEVEL": ("Как бы вы описали текущий уровень игрока?", get_baza_level_keyboard()),
|
||||||
|
"BAZA_FORMAT": ("Какой формат рассматриваете?", get_baza_format_keyboard()),
|
||||||
|
}
|
||||||
|
msg, kb = prompts[state]
|
||||||
|
db.update_user_state(user_id, state)
|
||||||
|
self.send_message(user_id, msg, keyboard=kb)
|
||||||
|
|
||||||
|
def _baza_advance(self, user_id: int, from_state=None):
|
||||||
|
"""Переходит к следующему шагу воронки, ПРОПУСКАЯ уже заполненные поля (§15)."""
|
||||||
|
p = db.get_profile(user_id)
|
||||||
|
start = 0 if from_state is None else self._FUNNEL_ORDER.index(from_state) + 1
|
||||||
|
for state in self._FUNNEL_ORDER[start:]:
|
||||||
|
field = self._FUNNEL_FIELD.get(state)
|
||||||
|
if not field or not p.get(field):
|
||||||
|
self._baza_ask(user_id, state)
|
||||||
|
return
|
||||||
|
self._baza_ask(user_id, "BAZA_FORMAT")
|
||||||
|
|
||||||
def _baza_start_funnel(self, user_id: int):
|
def _baza_start_funnel(self, user_id: int):
|
||||||
"""Старт воронки «Подобрать тренировку»."""
|
"""Старт воронки «Подобрать тренировку» (уже известные ответы не переспрашиваем)."""
|
||||||
self._baza_started(user_id)
|
self._baza_started(user_id)
|
||||||
db.set_profile_field(user_id, "source", "Основное меню")
|
db.set_profile_field(user_id, "source", "Основное меню")
|
||||||
db.add_metka(user_id, "source_main_menu")
|
db.add_metka(user_id, "source_main_menu")
|
||||||
db.update_user_state(user_id, "BAZA_FORWHO")
|
p = db.get_profile(user_id)
|
||||||
|
if any(p.get(f) for f in ("role", "age", "game", "level")):
|
||||||
self.send_message(
|
self.send_message(
|
||||||
user_id,
|
user_id,
|
||||||
"Для кого подбираем тренировки?",
|
"С возвращением! Что вы уже указывали, переспрашивать не буду. "
|
||||||
keyboard=get_baza_forwho_keyboard()
|
"Если нужно изменить прошлый ответ — нажмите «Назад ⬅️»."
|
||||||
)
|
)
|
||||||
|
self._baza_advance(user_id, None)
|
||||||
|
|
||||||
def _handle_baza_forwho(self, user_id: int, text: str, text_lower: str):
|
def _handle_baza_forwho(self, user_id: int, text: str, text_lower: str):
|
||||||
if self._baza_nav(user_id, text_lower):
|
if self._baza_nav(user_id, text_lower):
|
||||||
@@ -719,8 +750,7 @@ class VKBotApplication:
|
|||||||
else:
|
else:
|
||||||
self.send_message(user_id, "Выберите вариант на кнопках ниже 👇", keyboard=get_baza_forwho_keyboard())
|
self.send_message(user_id, "Выберите вариант на кнопках ниже 👇", keyboard=get_baza_forwho_keyboard())
|
||||||
return
|
return
|
||||||
db.update_user_state(user_id, "BAZA_AGE")
|
self._baza_advance(user_id, "BAZA_FORWHO")
|
||||||
self.send_message(user_id, "Сколько лет игроку?", keyboard=get_baza_age_keyboard())
|
|
||||||
|
|
||||||
def _handle_baza_age(self, user_id: int, text: str, text_lower: str):
|
def _handle_baza_age(self, user_id: int, text: str, text_lower: str):
|
||||||
if self._baza_nav(user_id, text_lower):
|
if self._baza_nav(user_id, text_lower):
|
||||||
@@ -750,8 +780,7 @@ class VKBotApplication:
|
|||||||
self.send_message(user_id, "Выберите возраст на кнопках ниже 👇", keyboard=get_baza_age_keyboard())
|
self.send_message(user_id, "Выберите возраст на кнопках ниже 👇", keyboard=get_baza_age_keyboard())
|
||||||
return
|
return
|
||||||
db.set_profile_field(user_id, "age", age)
|
db.set_profile_field(user_id, "age", age)
|
||||||
db.update_user_state(user_id, "BAZA_GAME")
|
self._baza_advance(user_id, "BAZA_AGE")
|
||||||
self.send_message(user_id, "По какой игре хотите заниматься?", keyboard=get_baza_game_keyboard())
|
|
||||||
|
|
||||||
def _handle_baza_young(self, user_id: int, text: str, text_lower: str):
|
def _handle_baza_young(self, user_id: int, text: str, text_lower: str):
|
||||||
if text_lower.startswith("обсудить"):
|
if text_lower.startswith("обсудить"):
|
||||||
@@ -793,8 +822,7 @@ class VKBotApplication:
|
|||||||
return
|
return
|
||||||
db.set_profile_field(user_id, "game", game)
|
db.set_profile_field(user_id, "game", game)
|
||||||
db.add_metka(user_id, metka)
|
db.add_metka(user_id, metka)
|
||||||
db.update_user_state(user_id, "BAZA_LEVEL")
|
self._baza_advance(user_id, "BAZA_GAME")
|
||||||
self.send_message(user_id, "Как бы вы описали текущий уровень игрока?", keyboard=get_baza_level_keyboard())
|
|
||||||
|
|
||||||
def _handle_baza_game_other(self, user_id: int, text: str, text_lower: str):
|
def _handle_baza_game_other(self, user_id: int, text: str, text_lower: str):
|
||||||
if self._baza_nav(user_id, text_lower):
|
if self._baza_nav(user_id, text_lower):
|
||||||
@@ -827,8 +855,7 @@ class VKBotApplication:
|
|||||||
self.send_message(user_id, "Выберите уровень на кнопках ниже 👇", keyboard=get_baza_level_keyboard())
|
self.send_message(user_id, "Выберите уровень на кнопках ниже 👇", keyboard=get_baza_level_keyboard())
|
||||||
return
|
return
|
||||||
db.set_profile_field(user_id, "level", match)
|
db.set_profile_field(user_id, "level", match)
|
||||||
db.update_user_state(user_id, "BAZA_FORMAT")
|
self._baza_advance(user_id, "BAZA_LEVEL")
|
||||||
self.send_message(user_id, "Какой формат рассматриваете?", keyboard=get_baza_format_keyboard())
|
|
||||||
|
|
||||||
def _handle_baza_format(self, user_id: int, text: str, text_lower: str):
|
def _handle_baza_format(self, user_id: int, text: str, text_lower: str):
|
||||||
if self._baza_nav(user_id, text_lower):
|
if self._baza_nav(user_id, text_lower):
|
||||||
@@ -985,6 +1012,12 @@ class VKBotApplication:
|
|||||||
if self._baza_nav(user_id, text_lower):
|
if self._baza_nav(user_id, text_lower):
|
||||||
return
|
return
|
||||||
if "участвовать" in text_lower or "записаться" in text_lower or "хочу" in text_lower or "да" == text_lower:
|
if "участвовать" in text_lower or "записаться" in text_lower or "хочу" in text_lower or "да" == text_lower:
|
||||||
|
age = (db.get_profile(user_id).get("age") or "").lower()
|
||||||
|
if age and "младше 12" not in age:
|
||||||
|
# возраст уже известен из подбора — не переспрашиваем (§15)
|
||||||
|
db.update_user_state(user_id, "BAZA_INT_LEVEL")
|
||||||
|
self.send_message(user_id, "Какой сейчас уровень в CS2?", keyboard=get_baza_int_level_keyboard())
|
||||||
|
else:
|
||||||
db.update_user_state(user_id, "BAZA_INT_AGE")
|
db.update_user_state(user_id, "BAZA_INT_AGE")
|
||||||
self.send_message(user_id, "Сколько лет игроку?", keyboard=get_baza_age_keyboard())
|
self.send_message(user_id, "Сколько лет игроку?", keyboard=get_baza_age_keyboard())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -62,6 +62,32 @@ def test_baza_other_game_goes_to_manager(app):
|
|||||||
assert "Valorant" in _prof(u)["game"]
|
assert "Valorant" in _prof(u)["game"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_baza_smart_skip_known_fields(app):
|
||||||
|
"""§15: уже сохранённые ответы не переспрашиваются."""
|
||||||
|
u = 70020
|
||||||
|
bot.db.set_profile_field(u, "role", "Игрок (пишет сам)")
|
||||||
|
bot.db.set_profile_field(u, "age", "16 лет и старше")
|
||||||
|
# роль и возраст известны → воронка стартует сразу с игры
|
||||||
|
app.handle_message(u, "Подобрать тренировку 🎯")
|
||||||
|
assert _state(u) == "BAZA_GAME"
|
||||||
|
# уровень известен → после игры сразу формат (пропуская уровень)
|
||||||
|
bot.db.set_profile_field(u, "level", "Играет для себя")
|
||||||
|
app.handle_message(u, "CS2")
|
||||||
|
assert _state(u) == "BAZA_FORMAT"
|
||||||
|
# «Назад» позволяет переопределить пропущенный уровень
|
||||||
|
app.handle_message(u, "Назад ⬅️")
|
||||||
|
assert _state(u) == "BAZA_LEVEL"
|
||||||
|
|
||||||
|
|
||||||
|
def test_baza_intensive_skips_known_age(app):
|
||||||
|
"""§15: в интенсиве известный возраст не переспрашивается."""
|
||||||
|
u = 70021
|
||||||
|
bot.db.set_profile_field(u, "age", "14–15 лет")
|
||||||
|
app.handle_message(u, "Летний интенсив CS2 🔥")
|
||||||
|
app.handle_message(u, "Хочу участвовать 🔥")
|
||||||
|
assert _state(u) == "BAZA_INT_LEVEL" # возраст пропущен
|
||||||
|
|
||||||
|
|
||||||
def test_baza_intensive_by_keyword(app):
|
def test_baza_intensive_by_keyword(app):
|
||||||
"""Кодовое слово «лето» запускает интенсив; ветка доходит до оплаты."""
|
"""Кодовое слово «лето» запускает интенсив; ветка доходит до оплаты."""
|
||||||
u = 70014
|
u = 70014
|
||||||
|
|||||||
Reference in New Issue
Block a user