This commit is contained in:
2026-02-20 00:58:21 +03:00
parent 0e111c31cb
commit bcc510824b
7 changed files with 107 additions and 4 deletions
+15 -2
View File
@@ -24,10 +24,23 @@
• Или отправьте команду /start
ШАГ 2: Вход в админ-панель
СПОСОБ 1: Получение прав администратора через пароль
• Отправьте команду /admin_login
• Введите пароль администратора (получите у создателя бота)
• При правильном пароле вы получите права администратора
• Создатель бота получит уведомление о новом администраторе
• После этого используйте команду /admin для входа в панель
СПОСОБ 2: Прямой вход (если уже администратор)
• Отправьте команду /admin
• Если вы администратор, откроется админ-панель
• Если команда не работает - обратитесь к создателю бота (@kolo_id)
• Только создатель бота имеет права назначать администраторов
• Если команда не работает - используйте /admin_login
⚠️ ВАЖНО:
• Лимит попыток ввода пароля: 5 попыток в час
• При превышении лимита нужно подождать
• Только создатель бота имеет права назначать администраторов напрямую
═══════════════════════════════════════════════════════════════════════════════
2. ГЛАВНОЕ МЕНЮ АДМИНКИ
+1
View File
@@ -1,6 +1,7 @@
[bot]
token = "1234567890"
creator_id = 1234567890
admin_password = "your_admin_password"
[security]
encode_key = "encode_key"
+2
View File
@@ -25,6 +25,7 @@ from quizzi.application.bot.user_dialogs.deeplink import deeplink_dialog
from quizzi.application.bot.user_dialogs.main_menu import user_menu_dialog
from quizzi.application.bot.user_dialogs.registration import registration_dialog
from quizzi.application.bot.user_dialogs.take_test import take_test_dialog
from quizzi.application.bot.user_dialogs.admin_login import admin_login_dialog
from quizzi.infrastructure.database.repo.test import TestRepository
from quizzi.infrastructure.database.repo.user import UserRepository
from quizzi.infrastructure.di import DatabaseProvider, SchedulerProvider, ServiceProvider
@@ -62,6 +63,7 @@ async def main() -> None:
take_test_dialog,
registration_dialog,
deeplink_dialog,
admin_login_dialog,
shared_tests_dialog,
shared_groups_dialog,
shared_broadcast_dialog,
+8 -1
View File
@@ -9,7 +9,7 @@ from dishka.integrations.aiogram import FromDishka
from quizzi.application.bot.admin_dialogs.states import AdminMenuSG
from quizzi.application.bot.creator_dialogs.states import CreatorMenuSG
from quizzi.application.bot.user_dialogs.states import UserDeeplinkSG, UserMenuSG, UserRegistrationSG
from quizzi.application.bot.user_dialogs.states import UserAdminLoginSG, UserDeeplinkSG, UserMenuSG, UserRegistrationSG
from quizzi.service.test import TestService
from quizzi.service.user import UserService
@@ -165,6 +165,13 @@ async def creator_command(_message: Message, dialog_manager: DialogManager) -> N
await dialog_manager.start(CreatorMenuSG.main, mode=StartMode.RESET_STACK)
@router.message(Command("admin_login"))
async def admin_login_command(_message: Message, dialog_manager: DialogManager) -> None:
assert _message.from_user is not None
logger.info("Admin login attempt: user_id=%d", _message.from_user.id)
await dialog_manager.start(UserAdminLoginSG.password_input, mode=StartMode.RESET_STACK)
@router.error()
async def dialog_error_handler(event: ErrorEvent, dialog_manager: DialogManager) -> None:
if isinstance(event.exception, (UnknownIntent, OutdatedIntent)):
@@ -0,0 +1,74 @@
from aiogram.types import Message
from aiogram_dialog import Dialog, DialogManager, Window
from aiogram_dialog.widgets.input import MessageInput
from aiogram_dialog.widgets.kbd import Button
from aiogram_dialog.widgets.text import Const
from dishka import FromDishka
from dishka.integrations.aiogram_dialog import inject
from quizzi.application.bot.user_dialogs.states import UserAdminLoginSG
from quizzi.infrastructure.database.dao.user import UserDAO
from quizzi.infrastructure.utils.config import Config
from quizzi.infrastructure.utils.rate_limiter import PasswordRateLimiter
password_limiter = PasswordRateLimiter()
@inject
async def on_password_input(
message: Message,
_widget: MessageInput,
manager: DialogManager,
user_dao: FromDishka[UserDAO],
config: FromDishka[Config],
):
assert message.from_user is not None
assert message.text is not None
user_id = message.from_user.id
allowed, wait_time = await password_limiter.check(user_id)
if not allowed:
minutes = int(wait_time // 60)
seconds = int(wait_time % 60)
await message.answer(
f"❌ Слишком много попыток. Попробуйте через {minutes} мин {seconds} сек"
)
return
password = message.text.strip()
if password == config.bot.admin_password:
await user_dao.update(user_id, is_admin=True)
await message.answer("✅ Вы успешно получили права администратора")
try:
await message.bot.send_message(
config.bot.creator_id,
f"🔔 Новый администратор:\n"
f"ID: {user_id}\n"
f"Username: @{message.from_user.username or 'нет'}\n"
f"Имя: {message.from_user.first_name}"
)
except Exception:
pass
await manager.done()
else:
await message.answer("❌ Неверный пароль")
async def on_cancel(_callback, _button, manager: DialogManager):
await manager.done()
admin_login_dialog = Dialog(
Window(
Const("<b>🔐 Вход в панель администратора</b>\n\n🔑 Введите пароль администратора:"),
MessageInput(on_password_input),
Button(Const("❌ Отмена"), id="cancel", on_click=on_cancel),
state=UserAdminLoginSG.password_input,
),
)
@@ -30,3 +30,7 @@ class UserDeeplinkSG(StatesGroup):
class UserRegistrationSG(StatesGroup):
input_name = State()
select_group = State()
class UserAdminLoginSG(StatesGroup):
password_input = State()
+3 -1
View File
@@ -8,6 +8,7 @@ from typing import Self
class BotConfig:
token: str
creator_id: int
admin_password: str
@dataclass
@@ -47,7 +48,8 @@ class Config:
return cls(
bot=BotConfig(
token=str(bot_data["token"]),
creator_id=int(bot_data["creator_id"])
creator_id=int(bot_data["creator_id"]),
admin_password=str(bot_data["admin_password"])
),
database=DatabaseConfig(
host=str(db_data["host"]),