Админка: показ/скрытие кнопок меню + экстренный стоп бота (/favi-stop)

1) Кнопки меню: раздел «Кнопки меню 🧩» в админке — показать/скрыть любую кнопку
   главного меню (Подобрать/Интенсив/Цены/Вопрос/Вебинар). Флаги в БД (bot_flags),
   дефолты в config.MENU_FEATURES (вебинар по умолчанию скрыт). Скрытый раздел не
   реагирует и на кодовые слова (интенсив выкл → «лето» игнорируется). Вебинар
   получил кнопку в меню (для теста ветки) + вход по callback из поста работает всегда.

2) Экстренное отключение (только супер-админ): /favi-stop → запрос пароля → бот
   уходит в аварийный режим и молчит для всех; /favi-start возвращает. Пароль хранится
   как PBKDF2-хэш в .env (FAVI_STOP_HASH), не в коде; сравнение константное, 3 попытки.

pytest 43 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kenik
2026-07-26 10:58:54 +03:00
parent 24daec1c09
commit d2ca750a2d
6 changed files with 279 additions and 36 deletions
+27 -10
View File
@@ -7,18 +7,34 @@ import config
# КЛАВИАТУРЫ ДЛЯ ИНТЕРФЕЙСА (UX/UI ВКонтакте)
# ==========================================
def get_main_keyboard(is_admin: bool = False) -> str:
"""Главное меню Кибершколы БАЗА. Для админа добавляется кнопка «Админка ⚙️»."""
def get_main_keyboard(is_admin: bool = False, flags: dict = None) -> str:
"""Главное меню Кибершколы БАЗА. flags — какие кнопки показывать (по умолчанию — все,
кроме вебинара). Для админа добавляется кнопка «Админка ⚙️»."""
f = {"pickup": True, "intensive": True, "prices": True, "question": True, "webinar": False}
if flags:
f.update(flags)
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)
has = False
def row(label, color=VkKeyboardColor.SECONDARY):
nonlocal has
if has:
keyboard.add_line()
keyboard.add_button(label, color=color)
has = True
if f.get("pickup"):
row("Подобрать тренировку 🎯", VkKeyboardColor.PRIMARY)
if f.get("intensive"):
row("Летний интенсив CS2 🔥")
if f.get("webinar"):
row("Записаться на вебинар 🎓")
if f.get("prices"):
row("Посмотреть цены 💰")
if f.get("question"):
row("Задать вопрос 💬")
if is_admin:
keyboard.add_line()
keyboard.add_button("Админка ⚙️", color=VkKeyboardColor.POSITIVE)
row("Админка ⚙️", VkKeyboardColor.POSITIVE)
return keyboard.get_keyboard()
@@ -156,6 +172,7 @@ def get_admin_keyboard(is_super: bool = False, muted: bool = False) -> str:
keyboard.add_button("Рассылка 📣", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("Ссылки 🔗", color=VkKeyboardColor.SECONDARY)
keyboard.add_line()
keyboard.add_button("Кнопки меню 🧩", color=VkKeyboardColor.SECONDARY)
keyboard.add_button("Уведомления: выкл 🔕" if muted else "Уведомления: вкл 🔔",
color=VkKeyboardColor.SECONDARY)
keyboard.add_line()