Тексты воронки по ТЗ + смена ссылок из админки
Тексты: - «Сколько лет игроку?» (было «…будущему ученику?») во всех местах; - сообщение для «Младше 12 лет» приведено дословно к ТЗ (§4); - убраны отсебятины-преамбулы, которых нет в ТЗ (приветствие, старт воронки, цены «Что дальше?», интенсив «Хотите записаться?»); кнопка младше-12 «Вернуться назад». Смена ссылок в админке: - таблица bot_links (key, url) + методы get_link/get_all_links/set_link; - _link(key): БД-переопределение → дефолт из config/.env; используется на шагах оплаты; - раздел «Ссылки 🔗» в админ-панели: список текущих адресов + «✏️ Изменить ссылку» (выбор из 8 кнопок → ввод URL с валидацией http/https). UTM добавляются автоматически. Тесты: добавлен test_admin_edit_link. pytest 31 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -247,7 +247,8 @@ class VKBotApplication:
|
|||||||
"Выберите раздел на кнопках ниже:\n"
|
"Выберите раздел на кнопках ниже:\n"
|
||||||
"• 📊 Статистика — заявки и диалоги\n"
|
"• 📊 Статистика — заявки и диалоги\n"
|
||||||
"• 💬 Быстрые ответы — авто-ответы по ключевым словам\n"
|
"• 💬 Быстрые ответы — авто-ответы по ключевым словам\n"
|
||||||
"• 📣 Рассылка — сообщение подписчикам"
|
"• 📣 Рассылка — сообщение подписчикам\n"
|
||||||
|
"• 🔗 Ссылки — куда ведут кнопки записи/оплаты"
|
||||||
+ extra,
|
+ extra,
|
||||||
keyboard=get_admin_keyboard(is_super=is_super)
|
keyboard=get_admin_keyboard(is_super=is_super)
|
||||||
)
|
)
|
||||||
@@ -444,6 +445,14 @@ class VKBotApplication:
|
|||||||
elif text_lower.startswith("рассылка"):
|
elif text_lower.startswith("рассылка"):
|
||||||
self._handle_admin_broadcast(user_id, text_clean)
|
self._handle_admin_broadcast(user_id, text_clean)
|
||||||
return
|
return
|
||||||
|
elif text_lower in ["ссылки 🔗", "ссылки", "ссылка"]:
|
||||||
|
self._send_links_admin(user_id)
|
||||||
|
return
|
||||||
|
elif text_lower in ["✏️ изменить ссылку", "изменить ссылку"]:
|
||||||
|
items = [(k, config.LINK_LABELS.get(k, k)) for k in config.LINK_LABELS]
|
||||||
|
db.update_user_state(user_id, "ADMIN_LINK_PICK")
|
||||||
|
self.send_message(user_id, "✏️ Какую ссылку изменить? Выберите:", keyboard=get_pick_keyboard(items))
|
||||||
|
return
|
||||||
elif text_lower in ["быстрые ответы 💬", "быстрые ответы", "фак"]:
|
elif text_lower in ["быстрые ответы 💬", "быстрые ответы", "фак"]:
|
||||||
self._send_faq_admin(user_id)
|
self._send_faq_admin(user_id)
|
||||||
return
|
return
|
||||||
@@ -663,10 +672,14 @@ class VKBotApplication:
|
|||||||
result = f"{base}{sep}{utm}"
|
result = f"{base}{sep}{utm}"
|
||||||
return f"{result}#{frag}" if sep_frag else result
|
return f"{result}#{frag}" if sep_frag else result
|
||||||
|
|
||||||
|
def _link(self, key: str) -> str:
|
||||||
|
"""Актуальный URL кнопки: переопределение из БД (админка) или дефолт из config/.env."""
|
||||||
|
return db.get_link(key) or config.LINKS.get(key, config.LINKS["tariffs"])
|
||||||
|
|
||||||
def _baza_send_openlink(self, user_id: int, link_key: str, message: str, button: str,
|
def _baza_send_openlink(self, user_id: int, link_key: str, message: str, button: str,
|
||||||
campaign: str = "main"):
|
campaign: str = "main"):
|
||||||
"""Отправляет сообщение с инлайн-кнопкой-ссылкой (без перевода на оплату/лид)."""
|
"""Отправляет сообщение с инлайн-кнопкой-ссылкой (без перевода на оплату/лид)."""
|
||||||
link = self._utm(config.LINKS.get(link_key, config.LINKS["tariffs"]), campaign)
|
link = self._utm(self._link(link_key), campaign)
|
||||||
kb = VkKeyboard(inline=True)
|
kb = VkKeyboard(inline=True)
|
||||||
kb.add_openlink_button(button, link)
|
kb.add_openlink_button(button, link)
|
||||||
self.send_message(user_id, message, keyboard=kb.get_keyboard())
|
self.send_message(user_id, message, keyboard=kb.get_keyboard())
|
||||||
@@ -682,7 +695,7 @@ class VKBotApplication:
|
|||||||
self._baza_started(user_id)
|
self._baza_started(user_id)
|
||||||
self.send_message(
|
self.send_message(
|
||||||
user_id,
|
user_id,
|
||||||
config.GREETING_BAZA + "\n\nВыберите действие на кнопках ниже 👇",
|
config.GREETING_BAZA,
|
||||||
keyboard=get_main_keyboard(is_admin=self._is_admin(user_id)),
|
keyboard=get_main_keyboard(is_admin=self._is_admin(user_id)),
|
||||||
attachment=self.get_logo_attachment(user_id)
|
attachment=self.get_logo_attachment(user_id)
|
||||||
)
|
)
|
||||||
@@ -712,7 +725,6 @@ class VKBotApplication:
|
|||||||
db.update_user_state(user_id, "BAZA_FORWHO")
|
db.update_user_state(user_id, "BAZA_FORWHO")
|
||||||
self.send_message(
|
self.send_message(
|
||||||
user_id,
|
user_id,
|
||||||
"Отлично! Задам пару коротких вопросов и подберу подходящий формат 🎯\n\n"
|
|
||||||
"Для кого подбираем тренировки?",
|
"Для кого подбираем тренировки?",
|
||||||
keyboard=get_baza_forwho_keyboard()
|
keyboard=get_baza_forwho_keyboard()
|
||||||
)
|
)
|
||||||
@@ -730,7 +742,7 @@ class VKBotApplication:
|
|||||||
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")
|
db.update_user_state(user_id, "BAZA_AGE")
|
||||||
self.send_message(user_id, "Сколько лет будущему ученику?", keyboard=get_baza_age_keyboard())
|
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):
|
||||||
@@ -741,12 +753,12 @@ class VKBotApplication:
|
|||||||
return
|
return
|
||||||
if "младше 12" in text_lower or "меньше 12" in text_lower:
|
if "младше 12" in text_lower or "меньше 12" in text_lower:
|
||||||
db.set_profile_field(user_id, "age", "Младше 12 лет")
|
db.set_profile_field(user_id, "age", "Младше 12 лет")
|
||||||
db.add_metka(user_id, "age_under12")
|
|
||||||
db.update_user_state(user_id, "BAZA_YOUNG")
|
db.update_user_state(user_id, "BAZA_YOUNG")
|
||||||
self.send_message(
|
self.send_message(
|
||||||
user_id,
|
user_id,
|
||||||
"Обычно мы занимаемся с ребятами от 12 лет. Если ученику чуть меньше — это не проблема, "
|
"Основные программы Кибершколы БАЗА рассчитаны на игроков от 12 лет. Но мы можем "
|
||||||
"лучше подобрать формат вместе с менеджером.",
|
"отдельно посмотреть, получится ли подобрать подходящий формат.\n"
|
||||||
|
"Передать запрос менеджеру?",
|
||||||
keyboard=get_baza_younger12_keyboard()
|
keyboard=get_baza_younger12_keyboard()
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -769,7 +781,7 @@ class VKBotApplication:
|
|||||||
return
|
return
|
||||||
if text_lower.startswith("назад") or text_lower.startswith("вернуться"):
|
if text_lower.startswith("назад") or text_lower.startswith("вернуться"):
|
||||||
db.update_user_state(user_id, "BAZA_AGE")
|
db.update_user_state(user_id, "BAZA_AGE")
|
||||||
self.send_message(user_id, "Сколько лет будущему ученику?", keyboard=get_baza_age_keyboard())
|
self.send_message(user_id, "Сколько лет игроку?", keyboard=get_baza_age_keyboard())
|
||||||
return
|
return
|
||||||
if self._baza_nav(user_id, text_lower):
|
if self._baza_nav(user_id, text_lower):
|
||||||
return
|
return
|
||||||
@@ -780,7 +792,7 @@ class VKBotApplication:
|
|||||||
return
|
return
|
||||||
if text_lower.startswith("назад"):
|
if text_lower.startswith("назад"):
|
||||||
db.update_user_state(user_id, "BAZA_AGE")
|
db.update_user_state(user_id, "BAZA_AGE")
|
||||||
self.send_message(user_id, "Сколько лет будущему ученику?", keyboard=get_baza_age_keyboard())
|
self.send_message(user_id, "Сколько лет игроку?", keyboard=get_baza_age_keyboard())
|
||||||
return
|
return
|
||||||
if "cs" in text_lower or "кс" in text_lower or "контр" in text_lower:
|
if "cs" in text_lower or "кс" in text_lower or "контр" in text_lower:
|
||||||
game, metka = "CS2", "game_cs2"
|
game, metka = "CS2", "game_cs2"
|
||||||
@@ -939,7 +951,7 @@ class VKBotApplication:
|
|||||||
|
|
||||||
def _baza_send_payment(self, user_id: int, link_key: str, tariff_label: str, campaign: str = "main"):
|
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)
|
link = self._utm(self._link(link_key), campaign)
|
||||||
db.set_profile_field(user_id, "tariff", tariff_label)
|
db.set_profile_field(user_id, "tariff", tariff_label)
|
||||||
db.add_metka(user_id, "clicked_payment")
|
db.add_metka(user_id, "clicked_payment")
|
||||||
db.add_metka(user_id, "lead_hot")
|
db.add_metka(user_id, "lead_hot")
|
||||||
@@ -987,7 +999,7 @@ class VKBotApplication:
|
|||||||
db.update_user_state(user_id, "BAZA_INT_INFO")
|
db.update_user_state(user_id, "BAZA_INT_INFO")
|
||||||
self.send_message(
|
self.send_message(
|
||||||
user_id,
|
user_id,
|
||||||
config.INTENSIVE_INFO + "\n\nХотите записаться на интенсив?",
|
config.INTENSIVE_INFO,
|
||||||
keyboard=get_baza_intensive_keyboard()
|
keyboard=get_baza_intensive_keyboard()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -996,7 +1008,7 @@ class VKBotApplication:
|
|||||||
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:
|
||||||
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
|
||||||
self.send_message(user_id, "Выберите действие на кнопках ниже 👇", keyboard=get_baza_intensive_keyboard())
|
self.send_message(user_id, "Выберите действие на кнопках ниже 👇", keyboard=get_baza_intensive_keyboard())
|
||||||
|
|
||||||
@@ -1005,7 +1017,7 @@ class VKBotApplication:
|
|||||||
return
|
return
|
||||||
if text_lower.startswith("назад"):
|
if text_lower.startswith("назад"):
|
||||||
db.update_user_state(user_id, "BAZA_INT_INFO")
|
db.update_user_state(user_id, "BAZA_INT_INFO")
|
||||||
self.send_message(user_id, "Хотите записаться на интенсив?", keyboard=get_baza_intensive_keyboard())
|
self.send_message(user_id, config.INTENSIVE_INFO, keyboard=get_baza_intensive_keyboard())
|
||||||
return
|
return
|
||||||
if "младше 12" in text_lower or "меньше 12" in text_lower:
|
if "младше 12" in text_lower or "меньше 12" in text_lower:
|
||||||
db.set_profile_field(user_id, "age", "Младше 12 лет")
|
db.set_profile_field(user_id, "age", "Младше 12 лет")
|
||||||
@@ -1113,7 +1125,7 @@ class VKBotApplication:
|
|||||||
|
|
||||||
def _baza_show_prices(self, user_id: int):
|
def _baza_show_prices(self, user_id: int):
|
||||||
db.update_user_state(user_id, "FREE_CHAT")
|
db.update_user_state(user_id, "FREE_CHAT")
|
||||||
self.send_message(user_id, config.PRICES_TEXT + "\n\nЧто дальше?", keyboard=get_baza_prices_keyboard())
|
self.send_message(user_id, config.PRICES_TEXT, keyboard=get_baza_prices_keyboard())
|
||||||
|
|
||||||
def _baza_manager_card(self, user_id: int, reason: str = "") -> str:
|
def _baza_manager_card(self, user_id: int, reason: str = "") -> str:
|
||||||
p = db.get_profile(user_id)
|
p = db.get_profile(user_id)
|
||||||
@@ -1383,6 +1395,41 @@ class VKBotApplication:
|
|||||||
back_to_faq(f"✅ Ответ №{fid} удалён.")
|
back_to_faq(f"✅ Ответ №{fid} удалён.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# ---------- Ссылки кнопок ----------
|
||||||
|
if state == "ADMIN_LINK_PICK":
|
||||||
|
key = pick_id(text)
|
||||||
|
if key not in config.LINK_LABELS:
|
||||||
|
self.send_message(user_id, "⚠️ Выберите ссылку кнопкой ниже.")
|
||||||
|
return
|
||||||
|
self._admin_ctx[user_id] = {"link_key": key}
|
||||||
|
db.update_user_state(user_id, "ADMIN_LINK_VAL")
|
||||||
|
self.send_message(
|
||||||
|
user_id,
|
||||||
|
f"✏️ «{config.LINK_LABELS[key]}»\nТекущий адрес:\n{self._link(key)}\n\n"
|
||||||
|
"Пришлите новый URL (начинается с http:// или https://). UTM добавится автоматически.",
|
||||||
|
keyboard=get_cancel_admin_keyboard()
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
if state == "ADMIN_LINK_VAL":
|
||||||
|
key = self._admin_ctx.get(user_id, {}).get("link_key")
|
||||||
|
url = text.strip()
|
||||||
|
if not key or key not in config.LINK_LABELS:
|
||||||
|
self._admin_ctx.pop(user_id, None)
|
||||||
|
db.update_user_state(user_id, "FREE_CHAT")
|
||||||
|
self._send_links_admin(user_id)
|
||||||
|
return
|
||||||
|
if not re.match(r"^https?://", url, re.I):
|
||||||
|
self.send_message(user_id, "⚠️ Ссылка должна начинаться с http:// или https://",
|
||||||
|
keyboard=get_cancel_admin_keyboard())
|
||||||
|
return
|
||||||
|
db.set_link(key, url)
|
||||||
|
self._admin_ctx.pop(user_id, None)
|
||||||
|
db.update_user_state(user_id, "FREE_CHAT")
|
||||||
|
self.send_message(user_id, f"✅ Ссылка «{config.LINK_LABELS[key]}» обновлена.")
|
||||||
|
self._send_links_admin(user_id)
|
||||||
|
return
|
||||||
|
|
||||||
# ---------- Администраторы (супер-админ) ----------
|
# ---------- Администраторы (супер-админ) ----------
|
||||||
if state == "ADMIN_ADMIN_ADD":
|
if state == "ADMIN_ADMIN_ADD":
|
||||||
if not self._is_super_admin(user_id):
|
if not self._is_super_admin(user_id):
|
||||||
@@ -1446,6 +1493,23 @@ class VKBotApplication:
|
|||||||
)
|
)
|
||||||
self.send_message(user_id, msg)
|
self.send_message(user_id, msg)
|
||||||
|
|
||||||
|
def _send_links_admin(self, user_id: int):
|
||||||
|
"""Раздел «Ссылки»: текущие адреса кнопок записи/оплаты + кнопка изменения."""
|
||||||
|
lines = []
|
||||||
|
for key, label in config.LINK_LABELS.items():
|
||||||
|
overridden = " (изменена)" if db.get_link(key) else ""
|
||||||
|
lines.append(f"🔗 {label}{overridden}\n {self._link(key)}")
|
||||||
|
kb = VkKeyboard(inline=True)
|
||||||
|
kb.add_button("✏️ Изменить ссылку", color=VkKeyboardColor.PRIMARY)
|
||||||
|
kb.add_line()
|
||||||
|
kb.add_button("В админку ↩️", color=VkKeyboardColor.SECONDARY)
|
||||||
|
self.send_message(
|
||||||
|
user_id,
|
||||||
|
"🔗 Ссылки кнопок записи/оплаты\n\n" + "\n\n".join(lines) +
|
||||||
|
"\n\nUTM-метки добавляются автоматически. Нажмите «Изменить ссылку», чтобы задать новый адрес.",
|
||||||
|
keyboard=kb.get_keyboard()
|
||||||
|
)
|
||||||
|
|
||||||
def _send_faq_admin(self, user_id: int):
|
def _send_faq_admin(self, user_id: int):
|
||||||
"""Раздел «Быстрые ответы»: список + инструкции."""
|
"""Раздел «Быстрые ответы»: список + инструкции."""
|
||||||
rows = db.get_custom_faq()
|
rows = db.get_custom_faq()
|
||||||
|
|||||||
@@ -109,6 +109,18 @@ LINKS = {
|
|||||||
"summer": os.getenv("LINK_SUMMER", "https://infocyber.pro/"), # оплата летнего интенсива
|
"summer": os.getenv("LINK_SUMMER", "https://infocyber.pro/"), # оплата летнего интенсива
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Человеческие названия ссылок (для админки смены ссылок). Порядок = порядок показа.
|
||||||
|
LINK_LABELS = {
|
||||||
|
"trial": "Запись на пробное",
|
||||||
|
"tariffs": "Страница тарифов",
|
||||||
|
"group_4": "Групповой абонемент — 4 занятия",
|
||||||
|
"group_8": "Групповой абонемент — 8 занятий",
|
||||||
|
"individual_1": "Индивидуальное — 1 занятие",
|
||||||
|
"individual_4": "Индивидуальные — 4 занятия",
|
||||||
|
"individual_8": "Индивидуальные — 8 занятий",
|
||||||
|
"summer": "Летний интенсив CS2",
|
||||||
|
}
|
||||||
|
|
||||||
# --- Триггеры запуска (регистронезависимо) ---
|
# --- Триггеры запуска (регистронезависимо) ---
|
||||||
MAIN_TRIGGERS = ["начать", "тренировки", "занятия", "цена", "стоимость", "записаться",
|
MAIN_TRIGGERS = ["начать", "тренировки", "занятия", "цена", "стоимость", "записаться",
|
||||||
"привет", "здравствуйте", "старт", "меню"]
|
"привет", "здравствуйте", "старт", "меню"]
|
||||||
|
|||||||
+37
-1
@@ -188,7 +188,43 @@ class DatabaseManager:
|
|||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
# Table for editable bot links (переопределяют дефолты из config/.env)
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS bot_links (
|
||||||
|
key TEXT PRIMARY KEY,
|
||||||
|
url TEXT NOT NULL,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
# ---------- Редактируемые ссылки (кнопки записи/оплаты) ----------
|
||||||
|
def get_link(self, key: str):
|
||||||
|
"""URL из БД (если админ переопределил) или None."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SELECT url FROM bot_links WHERE key = ?", (key,))
|
||||||
|
row = cursor.fetchone()
|
||||||
|
return row[0] if row else None
|
||||||
|
|
||||||
|
def get_all_links(self) -> Dict[str, str]:
|
||||||
|
"""Все переопределённые ссылки из БД в виде {key: url}."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("SELECT key, url FROM bot_links")
|
||||||
|
return {k: u for k, u in cursor.fetchall()}
|
||||||
|
|
||||||
|
def set_link(self, key: str, url: str):
|
||||||
|
"""Задать/обновить ссылку (переопределяет дефолт из config)."""
|
||||||
|
with self._get_connection() as conn:
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT INTO bot_links (key, url) VALUES (?, ?) "
|
||||||
|
"ON CONFLICT(key) DO UPDATE SET url = excluded.url, updated_at = CURRENT_TIMESTAMP",
|
||||||
|
(key, url)
|
||||||
|
)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
def get_user_data(self, user_id: int) -> Dict[str, Any]:
|
def get_user_data(self, user_id: int) -> Dict[str, Any]:
|
||||||
|
|||||||
+2
-1
@@ -63,7 +63,7 @@ def get_baza_younger12_keyboard() -> str:
|
|||||||
keyboard = VkKeyboard(one_time=False)
|
keyboard = VkKeyboard(one_time=False)
|
||||||
keyboard.add_button("Обсудить с менеджером 💬", color=VkKeyboardColor.SECONDARY)
|
keyboard.add_button("Обсудить с менеджером 💬", color=VkKeyboardColor.SECONDARY)
|
||||||
keyboard.add_line()
|
keyboard.add_line()
|
||||||
keyboard.add_button("Назад ⬅️", color=VkKeyboardColor.SECONDARY)
|
keyboard.add_button("Вернуться назад ↩️", color=VkKeyboardColor.SECONDARY)
|
||||||
keyboard.add_button("В главное меню ↩️", color=VkKeyboardColor.SECONDARY)
|
keyboard.add_button("В главное меню ↩️", color=VkKeyboardColor.SECONDARY)
|
||||||
return keyboard.get_keyboard()
|
return keyboard.get_keyboard()
|
||||||
|
|
||||||
@@ -149,6 +149,7 @@ def get_admin_keyboard(is_super: bool = False) -> str:
|
|||||||
keyboard.add_button("Быстрые ответы 💬", color=VkKeyboardColor.SECONDARY)
|
keyboard.add_button("Быстрые ответы 💬", color=VkKeyboardColor.SECONDARY)
|
||||||
keyboard.add_line()
|
keyboard.add_line()
|
||||||
keyboard.add_button("Рассылка 📣", color=VkKeyboardColor.SECONDARY)
|
keyboard.add_button("Рассылка 📣", color=VkKeyboardColor.SECONDARY)
|
||||||
|
keyboard.add_button("Ссылки 🔗", color=VkKeyboardColor.SECONDARY)
|
||||||
keyboard.add_line()
|
keyboard.add_line()
|
||||||
if is_super:
|
if is_super:
|
||||||
keyboard.add_button("Администраторы 👑", color=VkKeyboardColor.SECONDARY)
|
keyboard.add_button("Администраторы 👑", color=VkKeyboardColor.SECONDARY)
|
||||||
|
|||||||
@@ -139,6 +139,20 @@ def _admin():
|
|||||||
return bot.config.SUPER_ADMIN_IDS[0]
|
return bot.config.SUPER_ADMIN_IDS[0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_admin_edit_link(app):
|
||||||
|
"""Админ меняет ссылку кнопки → сохраняется в БД и подхватывается ботом."""
|
||||||
|
a = _admin()
|
||||||
|
app.handle_message(a, "Ссылки 🔗")
|
||||||
|
app.handle_message(a, "✏️ Изменить ссылку")
|
||||||
|
app.handle_message(a, "group_4. Групповой абонемент — 4 занятия")
|
||||||
|
app.handle_message(a, "не-ссылка") # отклоняется валидацией
|
||||||
|
assert bot.db.get_link("group_4") is None
|
||||||
|
app.handle_message(a, "https://infocyber.pro/pay/group4")
|
||||||
|
assert bot.db.get_link("group_4") == "https://infocyber.pro/pay/group4"
|
||||||
|
# бот отдаёт обновлённую ссылку (с UTM)
|
||||||
|
assert app._link("group_4") == "https://infocyber.pro/pay/group4"
|
||||||
|
|
||||||
|
|
||||||
def test_admin_faq_crud(app):
|
def test_admin_faq_crud(app):
|
||||||
a = _admin()
|
a = _admin()
|
||||||
app.handle_message(a, "Быстрые ответы 💬")
|
app.handle_message(a, "Быстрые ответы 💬")
|
||||||
|
|||||||
Reference in New Issue
Block a user