Ребрендинг БАЗА (фундамент): конфиг + профиль воронки в БД
Фаза 1 — config: BRAND «Кибершкола БАЗА», цены/тексты по ТЗ (приветствие, прайс, интенсив), 8 ссылок-плейсхолдеров с UTM, триггеры (основные/лето). Фаза 2 — database: JSON-профиль воронки (роль/возраст/игра/уровень/формат/ метки/…) + методы get/set/clear + add_metka. Живой флоу пока не затронут. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,63 @@ HR_MAIL_TO = os.getenv("HR_MAIL_TO", "") or MAIL_TO
|
|||||||
# True — порт 465 (SSL), False — STARTTLS (обычно порт 587)
|
# True — порт 465 (SSL), False — STARTTLS (обычно порт 587)
|
||||||
SMTP_USE_SSL = os.getenv("SMTP_USE_SSL", "true").strip().lower() in ("1", "true", "yes")
|
SMTP_USE_SSL = os.getenv("SMTP_USE_SSL", "true").strip().lower() in ("1", "true", "yes")
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# КИБЕРШКОЛА БАЗА — бренд, цены, ссылки, триггеры (по ТЗ)
|
||||||
|
# Всё редактируемое собрано здесь, чтобы менять без правки логики.
|
||||||
|
# ============================================================
|
||||||
|
BRAND = "Кибершкола БАЗА"
|
||||||
|
|
||||||
|
# --- Ссылки на оплату/запись (ПЛЕЙСХОЛДЕРЫ — заменить на реальные) ---
|
||||||
|
# UTM добавляются автоматически там, где ссылка ведёт наружу.
|
||||||
|
UTM_MAIN = "utm_source=vk&utm_medium=bot&utm_campaign=main_bot"
|
||||||
|
UTM_SUMMER = "utm_source=vk&utm_medium=bot&utm_campaign=summer_intensive"
|
||||||
|
LINKS = {
|
||||||
|
"trial": os.getenv("LINK_TRIAL", "https://infocyber.pro/"), # запись на пробное
|
||||||
|
"tariffs": os.getenv("LINK_TARIFFS", "https://infocyber.pro/"), # страница с тарифами
|
||||||
|
"group_4": os.getenv("LINK_GROUP_4", "https://infocyber.pro/"), # групповой абонемент 4
|
||||||
|
"group_8": os.getenv("LINK_GROUP_8", "https://infocyber.pro/"), # групповой абонемент 8
|
||||||
|
"individual_1": os.getenv("LINK_IND_1", "https://infocyber.pro/"), # индивидуальная 1
|
||||||
|
"individual_4": os.getenv("LINK_IND_4", "https://infocyber.pro/"), # индивидуальные 4
|
||||||
|
"individual_8": os.getenv("LINK_IND_8", "https://infocyber.pro/"), # индивидуальные 8
|
||||||
|
"summer": os.getenv("LINK_SUMMER", "https://infocyber.pro/"), # оплата летнего интенсива
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Триггеры запуска (регистронезависимо) ---
|
||||||
|
MAIN_TRIGGERS = ["начать", "тренировки", "занятия", "цена", "стоимость", "записаться",
|
||||||
|
"привет", "здравствуйте", "старт", "меню"]
|
||||||
|
SUMMER_TRIGGERS = ["лето", "интенсив", "летний интенсив", "интенсив cs2", "хочу на интенсив"]
|
||||||
|
|
||||||
|
# --- Тексты (по ТЗ, редактируемые) ---
|
||||||
|
GREETING_BAZA = (
|
||||||
|
"Привет! Это Кибершкола БАЗА 🎮\n\n"
|
||||||
|
"Мы проводим онлайн-тренировки по CS2, Dota 2 и World of Tanks для подростков 12+.\n"
|
||||||
|
"Помогу подобрать подходящий формат, посмотреть цены или записаться на тренировку."
|
||||||
|
)
|
||||||
|
|
||||||
|
PRICES_TEXT = (
|
||||||
|
"Стоимость тренировок в Кибершколе БАЗА:\n\n"
|
||||||
|
"🧑🤝🧑 Групповые тренировки:\n"
|
||||||
|
"• 4 занятия — 2 790 ₽\n"
|
||||||
|
"• 8 занятий — 4 990 ₽\n\n"
|
||||||
|
"👤 Индивидуальные тренировки:\n"
|
||||||
|
"• 1 занятие — 870 ₽\n"
|
||||||
|
"• 4 занятия — 3 290 ₽\n"
|
||||||
|
"• 8 занятий — 5 990 ₽\n\n"
|
||||||
|
"Все занятия проходят онлайн с тренером."
|
||||||
|
)
|
||||||
|
|
||||||
|
INTENSIVE_PRICE = "1 990 ₽"
|
||||||
|
INTENSIVE_INFO = (
|
||||||
|
"Привет! Вы написали кодовое слово «лето» — рассказываю про летний интенсив по CS2 🔥\n\n"
|
||||||
|
"Это 4 онлайн-тренировки по 90 минут с тренером.\n"
|
||||||
|
"На интенсиве участники будут:\n"
|
||||||
|
"— разбирать роли и командную игру;\n"
|
||||||
|
"— тренировать аим, дуэли и контрстрейфы;\n"
|
||||||
|
"— разбирать игровые моменты и демозаписи;\n"
|
||||||
|
"— изучать гранаты, мувмент и полезные игровые приёмы.\n\n"
|
||||||
|
f"Стоимость участия — {INTENSIVE_PRICE} за все 4 тренировки."
|
||||||
|
)
|
||||||
|
|
||||||
# Standard replies and patterns
|
# Standard replies and patterns
|
||||||
FAQ_ANSWERS = {
|
FAQ_ANSWERS = {
|
||||||
"цены": (
|
"цены": (
|
||||||
|
|||||||
+48
-1
@@ -30,7 +30,7 @@ class DatabaseManager:
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
# Migration checks for existing databases
|
# Migration checks for existing databases
|
||||||
for col in ["consent_1", "consent_2", "consent_3", "call_time", "vac_data"]:
|
for col in ["consent_1", "consent_2", "consent_3", "call_time", "vac_data", "profile"]:
|
||||||
try:
|
try:
|
||||||
cursor.execute(f"ALTER TABLE user_states ADD COLUMN {col} TEXT")
|
cursor.execute(f"ALTER TABLE user_states ADD COLUMN {col} TEXT")
|
||||||
except sqlite3.OperationalError:
|
except sqlite3.OperationalError:
|
||||||
@@ -475,6 +475,53 @@ class DatabaseManager:
|
|||||||
)
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
|
# --- Профиль воронки БАЗА (JSON в user_states.profile) ---
|
||||||
|
def get_profile(self, user_id: int) -> Dict[str, Any]:
|
||||||
|
"""Профиль пользователя из воронки (роль/возраст/игра/уровень/формат/метки/…)."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SELECT profile FROM user_states WHERE user_id = ?", (user_id,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
if row and row[0]:
|
||||||
|
try:
|
||||||
|
return json.loads(row[0])
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return {}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def set_profile_field(self, user_id: int, key: str, value: Any):
|
||||||
|
"""Обновить одно поле профиля (не спрашиваем повторно то, что уже есть)."""
|
||||||
|
data = self.get_profile(user_id)
|
||||||
|
data[key] = value
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO user_states (user_id, profile) VALUES (?, ?) "
|
||||||
|
"ON CONFLICT(user_id) DO UPDATE SET profile = excluded.profile, last_updated = CURRENT_TIMESTAMP",
|
||||||
|
(user_id, json.dumps(data, ensure_ascii=False))
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
def add_metka(self, user_id: int, metka: str):
|
||||||
|
"""Добавить метку пользователю (хранится списком в профиле, без повторов)."""
|
||||||
|
data = self.get_profile(user_id)
|
||||||
|
metki = data.get("metki", [])
|
||||||
|
if metka not in metki:
|
||||||
|
metki.append(metka)
|
||||||
|
data["metki"] = metki
|
||||||
|
self.set_profile_field(user_id, "metki", metki)
|
||||||
|
|
||||||
|
def clear_profile(self, user_id: int):
|
||||||
|
"""Полностью очистить профиль воронки."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO user_states (user_id, profile) VALUES (?, NULL) "
|
||||||
|
"ON CONFLICT(user_id) DO UPDATE SET profile = NULL, last_updated = CURRENT_TIMESTAMP",
|
||||||
|
(user_id,)
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
# --- Вакансии (направления игроков / должности сотрудников) ---
|
# --- Вакансии (направления игроков / должности сотрудников) ---
|
||||||
def get_vacancy_options(self, track: str) -> list:
|
def get_vacancy_options(self, track: str) -> list:
|
||||||
"""Активные опции для трека ('player'|'worker') как список (id, label)."""
|
"""Активные опции для трека ('player'|'worker') как список (id, label)."""
|
||||||
|
|||||||
Reference in New Issue
Block a user