diff --git a/src/dutylog/application/__main__.py b/src/dutylog/application/__main__.py index 7614850..ac19c60 100644 --- a/src/dutylog/application/__main__.py +++ b/src/dutylog/application/__main__.py @@ -10,6 +10,7 @@ from dishka.integrations.aiogram import setup_dishka from dutylog.application.bot.user_handlers import router as user_router from dutylog.application.bot.user_dialogs import main_menu_dialog +from dutylog.application.bot.user_dialogs.admin_dialogs import admin_menu_dialog from dutylog.infrastructure.ioc import ConfigProvider, DatabaseProvider, DAOProvider, RepositoryProvider from dutylog.infrastructure.utils.config import load_config @@ -34,6 +35,7 @@ async def main(): dp.include_router(user_router) dp.include_router(main_menu_dialog) + dp.include_router(admin_menu_dialog) setup_dialogs(dp) setup_dishka(container, dp, auto_inject=True) diff --git a/src/dutylog/application/bot/user_dialogs/admin_dialogs/__init__.py b/src/dutylog/application/bot/user_dialogs/admin_dialogs/__init__.py new file mode 100644 index 0000000..448aa9b --- /dev/null +++ b/src/dutylog/application/bot/user_dialogs/admin_dialogs/__init__.py @@ -0,0 +1,3 @@ +from dutylog.application.bot.user_dialogs.admin_dialogs.admin_menu_dialog import admin_menu_dialog + +__all__ = ["admin_menu_dialog"] diff --git a/src/dutylog/application/bot/user_dialogs/admin_dialogs/admin_menu_dialog.py b/src/dutylog/application/bot/user_dialogs/admin_dialogs/admin_menu_dialog.py new file mode 100644 index 0000000..f4899e8 --- /dev/null +++ b/src/dutylog/application/bot/user_dialogs/admin_dialogs/admin_menu_dialog.py @@ -0,0 +1,144 @@ +from aiogram.types import User +from aiogram_dialog import Dialog, Window, DialogManager +from aiogram_dialog.widgets.text import Format, Const +from aiogram_dialog.widgets.kbd import SwitchTo, Back, Start +from dishka import FromDishka +from dishka.integrations.aiogram_dialog import inject + +from dutylog.application.bot.user_dialogs.states import AdminMenuSG +from dutylog.infrastructure.database.repositories.users_repository import UsersRepository +from dutylog.infrastructure.utils.config import Config + + +@inject +async def get_admin_menu_data( + event_from_user: User, + users_repository: FromDishka[UsersRepository], + config: FromDishka[Config], + **kwargs, +): + user = await users_repository.get_or_create_user( + user_id=event_from_user.id, + username=event_from_user.username, + first_name=event_from_user.first_name, + last_name=event_from_user.last_name, + ) + + is_creator = event_from_user.id == config.bot.creator_id + + if is_creator: + greeting = "👑 Создатель" + else: + greeting = "👨‍💼 Администратор" + + content = f""" +{greeting} + +
📋 Панель управления
+ +Выберите действие: +""" + + return {"content": content} + + +@inject +async def get_users_list_data( + users_repository: FromDishka[UsersRepository], + **kwargs, +): + all_users = await users_repository.get_all_users() + + if not all_users: + users_text = """ +
👥 Список пользователей
+ +Пользователи не найдены +""" + else: + users_lines = [] + for user in all_users: + name = user.first_name or user.username or f"ID: {user.id}" + admin_badge = " 👨‍💼" if user.is_admin else "" + users_lines.append( + f"• {name}{admin_badge}\n" + f" 🟢 {user.active_hours} ч | 🔴 {user.inactive_hours} ч" + ) + + users_text = f""" +
👥 Список пользователей
+ +{"".join(f"{line}\n\n" for line in users_lines)} +Всего пользователей: {len(all_users)} +""" + + return {"users_content": users_text} + + +@inject +async def get_statistics_data( + users_repository: FromDishka[UsersRepository], + **kwargs, +): + all_users = await users_repository.get_all_users() + + total_users = len(all_users) + total_active_hours = sum(user.active_hours for user in all_users) + total_inactive_hours = sum(user.inactive_hours for user in all_users) + admins_count = len([user for user in all_users if user.is_admin]) + + stats_text = f""" +
📊 Статистика системы
+ +👥 Всего пользователей: {total_users} +👨‍💼 Администраторов: {admins_count} + +━━━━━━━━━━━━━━━━━━━━ + +🟢 Всего активных часов: {total_active_hours} ч +🔴 Всего неактивных часов: {total_inactive_hours} ч +📊 Общий итог: {total_active_hours + total_inactive_hours} ч +""" + + return {"stats_content": stats_text} + + +admin_menu_dialog = Dialog( + Window( + Format("{content}"), + SwitchTo( + Const("👥 Пользователи"), + id="users_btn", + state=AdminMenuSG.users_list, + ), + SwitchTo( + Const("📊 Статистика"), + id="stats_btn", + state=AdminMenuSG.statistics, + ), + SwitchTo( + Const("📢 Рассылка"), + id="broadcast_btn", + state=AdminMenuSG.broadcast, + ), + state=AdminMenuSG.main, + getter=get_admin_menu_data, + ), + Window( + Format("{users_content}"), + Back(Const("◀️ Назад")), + state=AdminMenuSG.users_list, + getter=get_users_list_data, + ), + Window( + Format("{stats_content}"), + Back(Const("◀️ Назад")), + state=AdminMenuSG.statistics, + getter=get_statistics_data, + ), + Window( + Const("
📢 Рассылка
\n\nФункционал в разработке"), + Back(Const("◀️ Назад")), + state=AdminMenuSG.broadcast, + ), +) diff --git a/src/dutylog/application/bot/user_dialogs/states.py b/src/dutylog/application/bot/user_dialogs/states.py index 9cd54db..8bdbff6 100644 --- a/src/dutylog/application/bot/user_dialogs/states.py +++ b/src/dutylog/application/bot/user_dialogs/states.py @@ -4,3 +4,10 @@ from aiogram.fsm.state import State, StatesGroup class MainMenuSG(StatesGroup): main = State() history = State() + + +class AdminMenuSG(StatesGroup): + main = State() + users_list = State() + statistics = State() + broadcast = State() diff --git a/src/dutylog/application/bot/user_handlers.py b/src/dutylog/application/bot/user_handlers.py index bfafa9e..aab1f97 100644 --- a/src/dutylog/application/bot/user_handlers.py +++ b/src/dutylog/application/bot/user_handlers.py @@ -2,12 +2,34 @@ from aiogram import Router from aiogram.filters import CommandStart from aiogram.types import Message from aiogram_dialog import DialogManager, StartMode +from dishka import FromDishka -from dutylog.application.bot.user_dialogs.states import MainMenuSG +from dutylog.application.bot.user_dialogs.states import MainMenuSG, AdminMenuSG +from dutylog.infrastructure.database.repositories.users_repository import UsersRepository +from dutylog.infrastructure.utils.config import Config router = Router() @router.message(CommandStart()) -async def start_handler(message: Message, dialog_manager: DialogManager): - await dialog_manager.start(MainMenuSG.main, mode=StartMode.RESET_STACK) +async def start_handler( + message: Message, + dialog_manager: DialogManager, + users_repository: FromDishka[UsersRepository], + config: FromDishka[Config], +): + assert message.from_user is not None + user = await users_repository.get_or_create_user( + user_id=message.from_user.id, + username=message.from_user.username, + first_name=message.from_user.first_name, + last_name=message.from_user.last_name, + ) + + is_creator = message.from_user.id == config.bot.creator_id + is_admin = user.is_admin + + if is_admin or is_creator: + await dialog_manager.start(AdminMenuSG.main, mode=StartMode.RESET_STACK) + else: + await dialog_manager.start(MainMenuSG.main, mode=StartMode.RESET_STACK)