Админка: показ/скрытие кнопок меню + экстренный стоп бота (/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:
@@ -88,6 +88,7 @@ def test_baza_question_ticket(app):
|
||||
def test_webinar_registration(app):
|
||||
"""Ветка вебинара: регистрация, метка, остаётся в боте (меню доступно)."""
|
||||
u = 70041
|
||||
bot.db.set_flag("webinar", True) # вебинар по умолчанию скрыт — включаем
|
||||
app.handle_message(u, "записаться на вебинар")
|
||||
assert "webinar_registered" in bot.db.get_profile(u).get("metki", [])
|
||||
assert _state(u) == "FREE_CHAT"
|
||||
@@ -247,6 +248,54 @@ def test_resolve_vk_links(app):
|
||||
assert app._resolve_vk_user_id("999") == 999
|
||||
|
||||
|
||||
def test_menu_feature_toggle(app):
|
||||
"""Скрытие кнопки меню: раздел «интенсив» выключается и не запускается по слову."""
|
||||
import json
|
||||
a = _admin()
|
||||
# выключаем интенсив
|
||||
app.handle_message(a, "Кнопки меню 🧩")
|
||||
app.handle_message(a, "Летний интенсив CS2 🔥 — вкл ✅") # нажатие -> выкл
|
||||
assert bot.db.get_flag("intensive", True) is False
|
||||
# кнопки нет в меню
|
||||
labels = [b["action"].get("label") for row in json.loads(app._menu_kb(a))["buttons"] for b in row]
|
||||
assert not any("Летний интенсив" in x for x in labels)
|
||||
# кодовое слово «лето» больше не запускает интенсив
|
||||
u = 70050
|
||||
app.handle_message(u, "лето")
|
||||
assert _state(u) == "FREE_CHAT"
|
||||
|
||||
|
||||
def test_favi_stop_start(app):
|
||||
"""Экстренное отключение супер-админом по паролю и обратное включение."""
|
||||
import config as cfg
|
||||
# тестовый хэш пароля 'secret'
|
||||
import hashlib, os
|
||||
salt = os.urandom(16)
|
||||
cfg.FAVI_STOP_HASH = "pbkdf2_sha256$240000$" + salt.hex() + "$" + \
|
||||
hashlib.pbkdf2_hmac("sha256", b"secret", salt, 240000).hex()
|
||||
a = bot.config.SUPER_ADMIN_IDS[0]
|
||||
app.handle_message(a, "/favi-stop")
|
||||
app.handle_message(a, "secret")
|
||||
assert bot.db.get_flag("bot_disabled", False) is True
|
||||
# обычный юзер не получает ответа
|
||||
app._sent.clear(); app.handle_message(70051, "привет")
|
||||
assert len(app._sent) == 0
|
||||
# включаем обратно
|
||||
app.handle_message(a, "/favi-start"); app.handle_message(a, "secret")
|
||||
assert bot.db.get_flag("bot_disabled", False) is False
|
||||
|
||||
|
||||
def test_favi_wrong_password(app):
|
||||
import config as cfg, hashlib, os
|
||||
salt = os.urandom(16)
|
||||
cfg.FAVI_STOP_HASH = "pbkdf2_sha256$240000$" + salt.hex() + "$" + \
|
||||
hashlib.pbkdf2_hmac("sha256", b"secret", salt, 240000).hex()
|
||||
a = bot.config.SUPER_ADMIN_IDS[0]
|
||||
app.handle_message(a, "/favi-stop")
|
||||
app.handle_message(a, "wrong")
|
||||
assert bot.db.get_flag("bot_disabled", False) is False # не отключился
|
||||
|
||||
|
||||
def test_admin_mute_notifications(app):
|
||||
"""Админ может отключить себе уведомления от клиентов — _notify_operators его пропускает."""
|
||||
op = _admin()
|
||||
|
||||
Reference in New Issue
Block a user