Files
bot_vk_ikp_prodagi/keyboards.py
T
Kenik 60199fb47b Ребрендинг БАЗА: новое меню + воронка подбора, интенсив, оплата, карточка менеджеру
Меню Кибершколы БАЗА (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>
2026-07-18 18:20:59 +03:00

383 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Клавиатуры интерфейса бота (VK)."""
from vk_api.keyboard import VkKeyboard, VkKeyboardColor
import config
# ==========================================
# КЛАВИАТУРЫ ДЛЯ ИНТЕРФЕЙСА (UX/UI ВКонтакте)
# ==========================================
def get_main_keyboard(is_admin: bool = False) -> str:
"""Главное меню Кибершколы БАЗА. Для админа добавляется кнопка «Админка ⚙️»."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Подобрать тренировку 🎯", color=VkKeyboardColor.PRIMARY)
keyboard.add_line()
keyboard.add_button("Летний интенсив CS2 🔥", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
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([["1213 лет", "1415 лет"], ["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)
keyboard.add_button("Статистика 📊", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("Кандидаты 👥", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Управление услугами ⚙️", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("Вакансии 💼", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Быстрые ответы 💬", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("Рассылка 📣", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
if is_super:
keyboard.add_button("Администраторы 👑", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("В главное меню ↩️", color=VkKeyboardColor.SECONDARY)
return keyboard.get_keyboard()
def get_svc_admin_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.NEGATIVE)
keyboard.add_line()
keyboard.add_button("В админку ↩️", color=VkKeyboardColor.SECONDARY)
return keyboard.get_keyboard()
def get_faq_admin_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.NEGATIVE)
keyboard.add_line()
keyboard.add_button("В админку ↩️", color=VkKeyboardColor.SECONDARY)
return keyboard.get_keyboard()
def get_pick_keyboard(items: list) -> str:
"""Клавиатура выбора из списка пар (id, label). Кнопка отправляет '<id>. <label>'."""
keyboard = VkKeyboard(one_time=False)
count = 0
for _id, label in items[:10]:
if count and count % 2 == 0:
keyboard.add_line()
short = label if len(label) <= 30 else label[:29] + ""
keyboard.add_button(f"{_id}. {short}", color=VkKeyboardColor.SECONDARY)
count += 1
keyboard.add_line()
keyboard.add_button("Отмена ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_cancel_admin_keyboard() -> str:
"""Кнопка отмены для админских мастеров ввода."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Отмена ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_vacancy_choice_keyboard() -> str:
"""Выбор направления в разделе 'Карьера в ИКП'."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Игроком в состав 🎮", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Сотрудником 💼", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Назад в меню ↩️", color=VkKeyboardColor.SECONDARY)
return keyboard.get_keyboard()
def get_vacancy_cancel_keyboard() -> str:
"""Клавиатура отмены отклика на свободном текстовом шаге."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Отменить ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_vacancy_back_keyboard() -> str:
"""Клавиатура с одной кнопкой «Назад в меню» — под карусель выбора в «Карьере»."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Назад в меню ↩️", color=VkKeyboardColor.SECONDARY)
return keyboard.get_keyboard()
def get_faq_keyboard() -> str:
"""Клавиатура раздела Частых Вопросов (FAQ)."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Цены 💰", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("Услуги ⚙️", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Контакты 📞", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("О компании 🏢", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Наши соцсети 🔗", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Назад в меню ↩️", color=VkKeyboardColor.SECONDARY)
return keyboard.get_keyboard()
def get_consent_docs_keyboard() -> str:
"""Инлайн-кнопки со ссылками на оферту и политику обработки ПД."""
keyboard = VkKeyboard(inline=True)
keyboard.add_openlink_button("📄 Оферта", getattr(config, "OFFER_URL", "https://infocyber.pro/"))
keyboard.add_line()
keyboard.add_openlink_button("🔐 Политика обработки ПД", getattr(config, "POLICY_URL", "https://infocyber.pro/"))
return keyboard.get_keyboard()
def get_socials_keyboard() -> str:
"""Клавиатура с кнопками-ссылками на площадки ИКП (открываются в один тап)."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_openlink_button("VK Live 📺", "https://live.vkvideo.ru/ikp")
keyboard.add_line()
keyboard.add_openlink_button("Telegram ✈️", "https://t.me/ikp_team")
keyboard.add_openlink_button("Lolka 🎮", "https://vk.com/away.php?to=https%3A%2F%2Flolka.gg%2FlNfM3F9DD&utf=1")
keyboard.add_line()
keyboard.add_openlink_button("ВКонтакте 🌐", "https://vk.com/ikpro")
keyboard.add_line()
keyboard.add_openlink_button("Наш сайт 🔗", "https://infocyber.pro/")
keyboard.add_line()
keyboard.add_button("Назад в меню ↩️", color=VkKeyboardColor.SECONDARY)
return keyboard.get_keyboard()
def get_cancel_keyboard() -> str:
"""Клавиатура отмены заполнения формы."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_operator_keyboard() -> str:
"""Клавиатура при активном диалоге с оператором."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Завершить диалог 🚪", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_binary_keyboard(show_skip: bool = False) -> str:
"""Клавиатура с кнопками Согласен / Не согласен."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Согласен(а) и подтверждаю ✅", color=VkKeyboardColor.PRIMARY)
keyboard.add_button("Не согласен(а) ❌", color=VkKeyboardColor.NEGATIVE)
if show_skip:
keyboard.add_line()
keyboard.add_button("Пропустить ➡️", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_returning_keyboard() -> str:
"""Нижняя клавиатура возвращающегося клиента: подтвердить или отменить."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Да, всё верно ✅", color=VkKeyboardColor.PRIMARY)
keyboard.add_line()
keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_returning_changes_keyboard() -> str:
"""Инлайн-кнопки под сообщением: сменить имя / телефон / заполнить заново."""
keyboard = VkKeyboard(inline=True)
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_calldate_keyboard() -> str:
"""Клавиатура выбора ДНЯ для звонка (сегодня/завтра + ближайшие даты)."""
from datetime import datetime, timedelta
wd = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]
today = datetime.now()
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Сегодня", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("Завтра", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
for i in (2, 3, 4):
d = today + timedelta(days=i)
keyboard.add_button(f"{wd[d.weekday()]} {d.strftime('%d.%m')}", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_calltime_keyboard() -> str:
"""Клавиатура выбора ВРЕМЕНИ (слоты в рабочие часы)."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("10:00", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("12:00", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("14:00", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("16:00", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("18:00", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("20:00", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Любое время", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_marketing_keyboard() -> str:
"""Клавиатура для необязательного согласия на маркетинговую рассылку."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Согласен на рассылку ✅", color=VkKeyboardColor.PRIMARY)
keyboard.add_button("Без рассылки ➡️", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_services_keyboard() -> str:
"""Клавиатура со списком доступных услуг."""
keyboard = VkKeyboard(one_time=False)
first = True
for key, svc in config.SERVICES.items():
if not first:
keyboard.add_line()
keyboard.add_button(svc["name"], color=VkKeyboardColor.SECONDARY)
first = False
keyboard.add_line()
keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_confirm_service_keyboard() -> str:
"""Клавиатура для подтверждения выбранной услуги."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Да, подтверждаю ✅", color=VkKeyboardColor.PRIMARY)
keyboard.add_button("Нет, выбрать другую 🔄", color=VkKeyboardColor.NEGATIVE)
keyboard.add_line()
keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_review_keyboard() -> str:
"""Клавиатура для финальной проверки данных пользователем."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Да, всё верно! ✅", color=VkKeyboardColor.PRIMARY)
keyboard.add_button("Изменить данные 🔄", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Отменить заполнение ❌", color=VkKeyboardColor.NEGATIVE)
return keyboard.get_keyboard()
def get_edit_menu_keyboard() -> str:
"""Клавиатура меню изменения полей."""
keyboard = VkKeyboard(one_time=False)
keyboard.add_button("Изменить Имя 👤", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("Изменить Телефон 📞", color=VkKeyboardColor.SECONDARY)
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()