mirror of
https://github.com/koloideal/Quizzi.git
synced 2026-06-10 18:35:28 +03:00
update
This commit is contained in:
@@ -25,7 +25,7 @@ async def on_groups_clicked(_callback: CallbackQuery, _button: Button, manager:
|
|||||||
|
|
||||||
|
|
||||||
async def on_broadcast_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager) -> None:
|
async def on_broadcast_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager) -> None:
|
||||||
await manager.start(SharedBroadcastSG.broadcast_input)
|
await manager.start(SharedBroadcastSG.select_groups)
|
||||||
|
|
||||||
|
|
||||||
async def on_templates_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager) -> None:
|
async def on_templates_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager) -> None:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ class AdminMenuSG(StatesGroup):
|
|||||||
|
|
||||||
class AdminUsersSG(StatesGroup):
|
class AdminUsersSG(StatesGroup):
|
||||||
users_list = State()
|
users_list = State()
|
||||||
|
filter_by_group = State()
|
||||||
users_input = State()
|
users_input = State()
|
||||||
user_detail = State()
|
user_detail = State()
|
||||||
user_stats = State()
|
user_stats = State()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from dishka import FromDishka
|
|||||||
from dishka.integrations.aiogram_dialog import inject
|
from dishka.integrations.aiogram_dialog import inject
|
||||||
|
|
||||||
from quizzi.application.bot.admin_dialogs.states import AdminUsersSG
|
from quizzi.application.bot.admin_dialogs.states import AdminUsersSG
|
||||||
|
from quizzi.infrastructure.database.dao.group import GroupDAO
|
||||||
from quizzi.infrastructure.database.dao.user import UserDAO
|
from quizzi.infrastructure.database.dao.user import UserDAO
|
||||||
from quizzi.infrastructure.database.repo.test import TestRepository
|
from quizzi.infrastructure.database.repo.test import TestRepository
|
||||||
from quizzi.infrastructure.database.repo.test_attempt import TestAttemptRepository
|
from quizzi.infrastructure.database.repo.test_attempt import TestAttemptRepository
|
||||||
@@ -14,19 +15,58 @@ from quizzi.infrastructure.utils.timezone import to_msk
|
|||||||
|
|
||||||
|
|
||||||
@inject
|
@inject
|
||||||
async def get_users_data(user_dao: FromDishka[UserDAO], **_kwargs):
|
async def get_users_data(dialog_manager: DialogManager, user_dao: FromDishka[UserDAO], group_dao: FromDishka[GroupDAO], **_kwargs):
|
||||||
users = await user_dao.get_all()
|
filter_group = dialog_manager.dialog_data.get("filter_group")
|
||||||
|
|
||||||
|
if filter_group:
|
||||||
|
users = await user_dao.get_by_groups([filter_group])
|
||||||
|
else:
|
||||||
|
users = await user_dao.get_all()
|
||||||
|
|
||||||
users_sorted = sorted(users, key=lambda u: u.created_at or u.id, reverse=True)
|
users_sorted = sorted(users, key=lambda u: u.created_at or u.id, reverse=True)
|
||||||
|
|
||||||
|
groups = await group_dao.get_all()
|
||||||
|
has_groups = len(groups) > 0
|
||||||
|
|
||||||
|
filter_text = f" (группа {filter_group})" if filter_group else ""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"users": [
|
"users": [
|
||||||
(f"{'👑 ' if u.is_admin else ''}{u.name or u.first_name} (@{u.username or 'нет'})", u.id)
|
(f"{'👑 ' if u.is_admin else ''}{u.name or u.first_name} (@{u.username or 'нет'})", u.id)
|
||||||
for u in users_sorted
|
for u in users_sorted
|
||||||
],
|
],
|
||||||
"count": len(users_sorted),
|
"count": len(users_sorted),
|
||||||
|
"has_groups": has_groups,
|
||||||
|
"filter_text": filter_text,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
async def get_groups_filter_data(group_dao: FromDishka[GroupDAO], **_kwargs):
|
||||||
|
groups = await group_dao.get_all()
|
||||||
|
return {
|
||||||
|
"groups": [(str(g.id), str(g.number)) for g in groups],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def on_filter_group_click(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||||
|
await manager.switch_to(AdminUsersSG.filter_by_group)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
async def on_group_filter_selected(_callback: CallbackQuery, _widget, manager: DialogManager, item_id: str, group_dao: FromDishka[GroupDAO]):
|
||||||
|
groups = await group_dao.get_all()
|
||||||
|
group = next((g for g in groups if str(g.id) == item_id), None)
|
||||||
|
if group:
|
||||||
|
manager.dialog_data["filter_group"] = group.number
|
||||||
|
await manager.switch_to(AdminUsersSG.users_list)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_clear_filter(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||||
|
manager.dialog_data.pop("filter_group", None)
|
||||||
|
await manager.switch_to(AdminUsersSG.users_list)
|
||||||
|
|
||||||
|
|
||||||
@inject
|
@inject
|
||||||
async def get_user_detail_data(dialog_manager: DialogManager, user_dao: FromDishka[UserDAO], **_kwargs):
|
async def get_user_detail_data(dialog_manager: DialogManager, user_dao: FromDishka[UserDAO], **_kwargs):
|
||||||
user_id = dialog_manager.dialog_data.get("selected_user_id")
|
user_id = dialog_manager.dialog_data.get("selected_user_id")
|
||||||
@@ -207,7 +247,7 @@ async def get_user_result_detail(
|
|||||||
|
|
||||||
admin_users_dialog = Dialog(
|
admin_users_dialog = Dialog(
|
||||||
Window(
|
Window(
|
||||||
Format("<b>👥 Пользователи</b>\n\nВсего: {count}"),
|
Format("<b>👥 Пользователи</b>{filter_text}\n\nВсего: {count}"),
|
||||||
ScrollingGroup(
|
ScrollingGroup(
|
||||||
Select(
|
Select(
|
||||||
Format("{item[0]}"),
|
Format("{item[0]}"),
|
||||||
@@ -221,12 +261,34 @@ admin_users_dialog = Dialog(
|
|||||||
height=7,
|
height=7,
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
|
Button(Const("🔍 Фильтр по группе"), id="filter_group", on_click=on_filter_group_click, when="has_groups"),
|
||||||
Button(Const("✏️ Ввести ID/Username"), id="input_mode", on_click=on_input_mode),
|
Button(Const("✏️ Ввести ID/Username"), id="input_mode", on_click=on_input_mode),
|
||||||
Button(Const("◀️ Назад"), id="back", on_click=on_back_to_main),
|
Button(Const("◀️ Назад"), id="back", on_click=on_back_to_main),
|
||||||
),
|
),
|
||||||
state=AdminUsersSG.users_list,
|
state=AdminUsersSG.users_list,
|
||||||
getter=get_users_data,
|
getter=get_users_data,
|
||||||
),
|
),
|
||||||
|
Window(
|
||||||
|
Const("<b>🔍 Фильтр по группе</b>\n\nВыберите группу:"),
|
||||||
|
ScrollingGroup(
|
||||||
|
Select(
|
||||||
|
Format("{item[1]}"),
|
||||||
|
id="group_filter_select",
|
||||||
|
item_id_getter=lambda x: x[0],
|
||||||
|
items="groups",
|
||||||
|
on_click=on_group_filter_selected,
|
||||||
|
),
|
||||||
|
id="groups_filter_scroll",
|
||||||
|
width=2,
|
||||||
|
height=5,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
Button(Const("🗑 Сбросить фильтр"), id="clear_filter", on_click=on_clear_filter),
|
||||||
|
SwitchTo(Const("◀️ Назад"), id="back_to_list", state=AdminUsersSG.users_list),
|
||||||
|
),
|
||||||
|
state=AdminUsersSG.filter_by_group,
|
||||||
|
getter=get_groups_filter_data,
|
||||||
|
),
|
||||||
Window(
|
Window(
|
||||||
Const("<b>Введите ID или @username пользователя:</b>"),
|
Const("<b>Введите ID или @username пользователя:</b>"),
|
||||||
MessageInput(on_user_input),
|
MessageInput(on_user_input),
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ async def on_groups_clicked(_callback: CallbackQuery, _button: Button, manager:
|
|||||||
|
|
||||||
|
|
||||||
async def on_broadcast_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager) -> None:
|
async def on_broadcast_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager) -> None:
|
||||||
await manager.start(SharedBroadcastSG.broadcast_input)
|
await manager.start(SharedBroadcastSG.select_groups)
|
||||||
|
|
||||||
|
|
||||||
async def on_templates_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager) -> None:
|
async def on_templates_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager) -> None:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ class CreatorMenuSG(StatesGroup):
|
|||||||
|
|
||||||
class CreatorUsersSG(StatesGroup):
|
class CreatorUsersSG(StatesGroup):
|
||||||
users_list = State()
|
users_list = State()
|
||||||
|
filter_by_group = State()
|
||||||
users_input = State()
|
users_input = State()
|
||||||
user_detail = State()
|
user_detail = State()
|
||||||
user_stats = State()
|
user_stats = State()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from dishka import FromDishka
|
|||||||
from dishka.integrations.aiogram_dialog import inject
|
from dishka.integrations.aiogram_dialog import inject
|
||||||
|
|
||||||
from quizzi.application.bot.creator_dialogs.states import CreatorUsersSG
|
from quizzi.application.bot.creator_dialogs.states import CreatorUsersSG
|
||||||
|
from quizzi.infrastructure.database.dao.group import GroupDAO
|
||||||
from quizzi.infrastructure.database.dao.user import UserDAO
|
from quizzi.infrastructure.database.dao.user import UserDAO
|
||||||
from quizzi.infrastructure.database.repo.test import TestRepository
|
from quizzi.infrastructure.database.repo.test import TestRepository
|
||||||
from quizzi.infrastructure.database.repo.test_attempt import TestAttemptRepository
|
from quizzi.infrastructure.database.repo.test_attempt import TestAttemptRepository
|
||||||
@@ -20,19 +21,58 @@ from quizzi.infrastructure.utils.timezone import to_msk
|
|||||||
|
|
||||||
|
|
||||||
@inject
|
@inject
|
||||||
async def get_users_data(user_dao: FromDishka[UserDAO], **_kwargs):
|
async def get_users_data(dialog_manager: DialogManager, user_dao: FromDishka[UserDAO], group_dao: FromDishka[GroupDAO], **_kwargs):
|
||||||
users = await user_dao.get_all()
|
filter_group = dialog_manager.dialog_data.get("filter_group")
|
||||||
|
|
||||||
|
if filter_group:
|
||||||
|
users = await user_dao.get_by_groups([filter_group])
|
||||||
|
else:
|
||||||
|
users = await user_dao.get_all()
|
||||||
|
|
||||||
users_sorted = sorted(users, key=lambda u: u.created_at or u.id, reverse=True)
|
users_sorted = sorted(users, key=lambda u: u.created_at or u.id, reverse=True)
|
||||||
|
|
||||||
|
groups = await group_dao.get_all()
|
||||||
|
has_groups = len(groups) > 0
|
||||||
|
|
||||||
|
filter_text = f" (группа {filter_group})" if filter_group else ""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"users": [
|
"users": [
|
||||||
(f"{'👑 ' if u.is_admin else ''}{u.name or u.first_name} (@{u.username or 'нет'})", u.id)
|
(f"{'👑 ' if u.is_admin else ''}{u.name or u.first_name} (@{u.username or 'нет'})", u.id)
|
||||||
for u in users_sorted
|
for u in users_sorted
|
||||||
],
|
],
|
||||||
"count": len(users_sorted),
|
"count": len(users_sorted),
|
||||||
|
"has_groups": has_groups,
|
||||||
|
"filter_text": filter_text,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
async def get_groups_filter_data(group_dao: FromDishka[GroupDAO], **_kwargs):
|
||||||
|
groups = await group_dao.get_all()
|
||||||
|
return {
|
||||||
|
"groups": [(str(g.id), str(g.number)) for g in groups],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def on_filter_group_click(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||||
|
await manager.switch_to(CreatorUsersSG.filter_by_group)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
async def on_group_filter_selected(_callback: CallbackQuery, _widget, manager: DialogManager, item_id: str, group_dao: FromDishka[GroupDAO]):
|
||||||
|
groups = await group_dao.get_all()
|
||||||
|
group = next((g for g in groups if str(g.id) == item_id), None)
|
||||||
|
if group:
|
||||||
|
manager.dialog_data["filter_group"] = group.number
|
||||||
|
await manager.switch_to(CreatorUsersSG.users_list)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_clear_filter(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||||
|
manager.dialog_data.pop("filter_group", None)
|
||||||
|
await manager.switch_to(CreatorUsersSG.users_list)
|
||||||
|
|
||||||
|
|
||||||
@inject
|
@inject
|
||||||
async def get_user_detail_data(dialog_manager: DialogManager, user_dao: FromDishka[UserDAO], **_kwargs):
|
async def get_user_detail_data(dialog_manager: DialogManager, user_dao: FromDishka[UserDAO], **_kwargs):
|
||||||
user_id = dialog_manager.dialog_data.get("selected_user_id")
|
user_id = dialog_manager.dialog_data.get("selected_user_id")
|
||||||
@@ -331,7 +371,7 @@ async def get_user_result_detail(
|
|||||||
|
|
||||||
creator_users_dialog = Dialog(
|
creator_users_dialog = Dialog(
|
||||||
Window(
|
Window(
|
||||||
Format("<b>👥 Пользователи</b>\n\nВсего: {count}"),
|
Format("<b>👥 Пользователи</b>{filter_text}\n\nВсего: {count}"),
|
||||||
ScrollingGroup(
|
ScrollingGroup(
|
||||||
Select(
|
Select(
|
||||||
Format("{item[0]}"),
|
Format("{item[0]}"),
|
||||||
@@ -345,12 +385,34 @@ creator_users_dialog = Dialog(
|
|||||||
height=7,
|
height=7,
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
|
Button(Const("🔍 Фильтр по группе"), id="filter_group", on_click=on_filter_group_click, when="has_groups"),
|
||||||
Button(Const("✏️ Ввести ID/Username"), id="input_mode", on_click=on_input_mode),
|
Button(Const("✏️ Ввести ID/Username"), id="input_mode", on_click=on_input_mode),
|
||||||
Button(Const("◀️ Назад"), id="back", on_click=on_back_to_main),
|
Button(Const("◀️ Назад"), id="back", on_click=on_back_to_main),
|
||||||
),
|
),
|
||||||
state=CreatorUsersSG.users_list,
|
state=CreatorUsersSG.users_list,
|
||||||
getter=get_users_data,
|
getter=get_users_data,
|
||||||
),
|
),
|
||||||
|
Window(
|
||||||
|
Const("<b>🔍 Фильтр по группе</b>\n\nВыберите группу:"),
|
||||||
|
ScrollingGroup(
|
||||||
|
Select(
|
||||||
|
Format("{item[1]}"),
|
||||||
|
id="group_filter_select",
|
||||||
|
item_id_getter=lambda x: x[0],
|
||||||
|
items="groups",
|
||||||
|
on_click=on_group_filter_selected,
|
||||||
|
),
|
||||||
|
id="groups_filter_scroll",
|
||||||
|
width=2,
|
||||||
|
height=5,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
Button(Const("🗑 Сбросить фильтр"), id="clear_filter", on_click=on_clear_filter),
|
||||||
|
SwitchTo(Const("◀️ Назад"), id="back_to_list", state=CreatorUsersSG.users_list),
|
||||||
|
),
|
||||||
|
state=CreatorUsersSG.filter_by_group,
|
||||||
|
getter=get_groups_filter_data,
|
||||||
|
),
|
||||||
Window(
|
Window(
|
||||||
Const("<b>Введите ID или @username пользователя:</b>"),
|
Const("<b>Введите ID или @username пользователя:</b>"),
|
||||||
MessageInput(on_user_input),
|
MessageInput(on_user_input),
|
||||||
|
|||||||
@@ -1,14 +1,66 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from aiogram.types import CallbackQuery, Message
|
from aiogram.types import CallbackQuery, Message
|
||||||
from aiogram_dialog import Dialog, DialogManager, Window
|
from aiogram_dialog import Dialog, DialogManager, Window
|
||||||
from aiogram_dialog.widgets.input import MessageInput
|
from aiogram_dialog.widgets.input import MessageInput
|
||||||
from aiogram_dialog.widgets.kbd import Button, Row
|
from aiogram_dialog.widgets.kbd import Button, Column, Multiselect, Row, ScrollingGroup
|
||||||
from aiogram_dialog.widgets.text import Const
|
from aiogram_dialog.widgets.text import Const, Format
|
||||||
from dishka import FromDishka
|
from dishka import FromDishka
|
||||||
from dishka.integrations.aiogram_dialog import inject
|
from dishka.integrations.aiogram_dialog import inject
|
||||||
|
|
||||||
from quizzi.application.bot.shared_dialogs.states import SharedBroadcastSG
|
from quizzi.application.bot.shared_dialogs.states import SharedBroadcastSG
|
||||||
|
from quizzi.infrastructure.database.dao.group import GroupDAO
|
||||||
from quizzi.service.broadcast import BroadcastService
|
from quizzi.service.broadcast import BroadcastService
|
||||||
|
|
||||||
|
from aiogram_dialog.widgets.kbd.select import ManagedMultiselect
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
async def get_groups_data(group_dao: FromDishka[GroupDAO], **_kwargs):
|
||||||
|
groups = await group_dao.get_all()
|
||||||
|
return {
|
||||||
|
"groups": [(str(g.id), str(g.number)) for g in groups],
|
||||||
|
"has_groups": len(groups) > 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def on_group_selected(
|
||||||
|
_callback: CallbackQuery,
|
||||||
|
_widget,
|
||||||
|
manager: DialogManager,
|
||||||
|
_item_id: str,
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
async def on_send_to_selected(
|
||||||
|
_callback: CallbackQuery,
|
||||||
|
_button: Button,
|
||||||
|
manager: DialogManager,
|
||||||
|
group_dao: FromDishka[GroupDAO],
|
||||||
|
):
|
||||||
|
multiselect: ManagedMultiselect[str] = manager.find("groups_multiselect") # type: ignore[assignment]
|
||||||
|
selected_ids = multiselect.get_checked()
|
||||||
|
|
||||||
|
if not selected_ids:
|
||||||
|
await _callback.answer("❌ Выберите хотя бы одну группу", show_alert=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
groups = await group_dao.get_all()
|
||||||
|
id_to_number = {str(g.id): g.number for g in groups}
|
||||||
|
selected_numbers = [id_to_number[gid] for gid in selected_ids if gid in id_to_number]
|
||||||
|
|
||||||
|
manager.dialog_data["selected_group_numbers"] = selected_numbers
|
||||||
|
manager.dialog_data["broadcast_to_all"] = False
|
||||||
|
await manager.switch_to(SharedBroadcastSG.broadcast_input)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_send_to_all(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||||
|
manager.dialog_data["selected_group_numbers"] = None
|
||||||
|
manager.dialog_data["broadcast_to_all"] = True
|
||||||
|
await manager.switch_to(SharedBroadcastSG.broadcast_input)
|
||||||
|
|
||||||
|
|
||||||
async def on_broadcast_input(message: Message, _widget: MessageInput, manager: DialogManager):
|
async def on_broadcast_input(message: Message, _widget: MessageInput, manager: DialogManager):
|
||||||
manager.dialog_data["broadcast_message_id"] = message.message_id
|
manager.dialog_data["broadcast_message_id"] = message.message_id
|
||||||
@@ -16,6 +68,19 @@ async def on_broadcast_input(message: Message, _widget: MessageInput, manager: D
|
|||||||
await manager.switch_to(SharedBroadcastSG.broadcast_confirm)
|
await manager.switch_to(SharedBroadcastSG.broadcast_confirm)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_confirm_data(dialog_manager: DialogManager, **_kwargs):
|
||||||
|
broadcast_to_all = dialog_manager.dialog_data.get("broadcast_to_all", False)
|
||||||
|
selected_numbers = dialog_manager.dialog_data.get("selected_group_numbers", [])
|
||||||
|
|
||||||
|
if broadcast_to_all:
|
||||||
|
target_text = "всем пользователям"
|
||||||
|
else:
|
||||||
|
groups_str = ", ".join(str(n) for n in selected_numbers)
|
||||||
|
target_text = f"группам: {groups_str}"
|
||||||
|
|
||||||
|
return {"target_text": target_text}
|
||||||
|
|
||||||
|
|
||||||
@inject
|
@inject
|
||||||
async def on_broadcast_confirm(
|
async def on_broadcast_confirm(
|
||||||
_callback: CallbackQuery,
|
_callback: CallbackQuery,
|
||||||
@@ -37,7 +102,8 @@ async def on_broadcast_confirm(
|
|||||||
await _callback.answer("Ошибка: бот не найден")
|
await _callback.answer("Ошибка: бот не найден")
|
||||||
return
|
return
|
||||||
|
|
||||||
stats = await broadcast_service.broadcast_message(bot, message_id, chat_id)
|
group_numbers = manager.dialog_data.get("selected_group_numbers")
|
||||||
|
stats = await broadcast_service.broadcast_message(bot, message_id, chat_id, group_numbers)
|
||||||
|
|
||||||
stats_text = (
|
stats_text = (
|
||||||
f"✅ <b>Рассылка завершена</b>\n\n"
|
f"✅ <b>Рассылка завершена</b>\n\n"
|
||||||
@@ -55,23 +121,52 @@ async def on_broadcast_cancel(_callback: CallbackQuery, _button: Button, manager
|
|||||||
await manager.done()
|
await manager.done()
|
||||||
|
|
||||||
|
|
||||||
|
async def on_back_to_groups(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||||
|
await manager.switch_to(SharedBroadcastSG.select_groups)
|
||||||
|
|
||||||
|
|
||||||
async def on_back_to_main(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
async def on_back_to_main(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||||
await manager.done()
|
await manager.done()
|
||||||
|
|
||||||
|
|
||||||
shared_broadcast_dialog = Dialog(
|
shared_broadcast_dialog = Dialog(
|
||||||
Window(
|
Window(
|
||||||
Const("<b>📢 Рассылка</b>\n\nОтправьте сообщение, которое хотите разослать всем пользователям:"),
|
Const("<b>📢 Рассылка</b>\n\n<b>Выберите группы для рассылки:</b>"),
|
||||||
|
ScrollingGroup(
|
||||||
|
Multiselect(
|
||||||
|
Format("✅ {item[1]}"),
|
||||||
|
Format("⬜ {item[1]}"),
|
||||||
|
id="groups_multiselect",
|
||||||
|
item_id_getter=lambda x: x[0],
|
||||||
|
items="groups",
|
||||||
|
on_click=on_group_selected,
|
||||||
|
),
|
||||||
|
id="groups_scroll",
|
||||||
|
width=2,
|
||||||
|
height=5,
|
||||||
|
when="has_groups",
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
Button(Const("📤 Отправить выбранным"), id="send_selected", on_click=on_send_to_selected, when="has_groups"),
|
||||||
|
Button(Const("📢 Отправить всем"), id="send_all", on_click=on_send_to_all),
|
||||||
|
Button(Const("◀️ Назад"), id="back", on_click=on_back_to_main),
|
||||||
|
),
|
||||||
|
state=SharedBroadcastSG.select_groups,
|
||||||
|
getter=get_groups_data,
|
||||||
|
),
|
||||||
|
Window(
|
||||||
|
Const("<b>📢 Рассылка</b>\n\nОтправьте сообщение, которое хотите разослать:"),
|
||||||
MessageInput(on_broadcast_input),
|
MessageInput(on_broadcast_input),
|
||||||
Button(Const("◀️ Отмена"), id="back", on_click=on_back_to_main),
|
Button(Const("◀️ Назад"), id="back", on_click=on_back_to_groups),
|
||||||
state=SharedBroadcastSG.broadcast_input,
|
state=SharedBroadcastSG.broadcast_input,
|
||||||
),
|
),
|
||||||
Window(
|
Window(
|
||||||
Const("<b>⚠️ Подтверждение рассылки</b>\n\nВы уверены, что хотите отправить это сообщение всем пользователям?"),
|
Format("<b>⚠️ Подтверждение рассылки</b>\n\nВы уверены, что хотите отправить это сообщение {target_text}?"),
|
||||||
Row(
|
Row(
|
||||||
Button(Const("✅ Да"), id="broadcast_confirm", on_click=on_broadcast_confirm),
|
Button(Const("✅ Да"), id="broadcast_confirm", on_click=on_broadcast_confirm),
|
||||||
Button(Const("❌ Нет"), id="broadcast_cancel", on_click=on_broadcast_cancel),
|
Button(Const("❌ Нет"), id="broadcast_cancel", on_click=on_broadcast_cancel),
|
||||||
),
|
),
|
||||||
state=SharedBroadcastSG.broadcast_confirm,
|
state=SharedBroadcastSG.broadcast_confirm,
|
||||||
|
getter=get_confirm_data,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from datetime import date, datetime, time
|
|||||||
from aiogram.types import CallbackQuery, ContentType, Message
|
from aiogram.types import CallbackQuery, ContentType, Message
|
||||||
from aiogram_dialog import Dialog, DialogManager, StartMode, Window
|
from aiogram_dialog import Dialog, DialogManager, StartMode, Window
|
||||||
from aiogram_dialog.widgets.input import MessageInput
|
from aiogram_dialog.widgets.input import MessageInput
|
||||||
from aiogram_dialog.widgets.kbd import Button, Calendar, Cancel, Column, Row, ScrollingGroup, Select
|
from aiogram_dialog.widgets.kbd import Button, Calendar, Column, Row, ScrollingGroup, Select
|
||||||
from aiogram_dialog.widgets.text import Const, Format
|
from aiogram_dialog.widgets.text import Const, Format
|
||||||
from dishka import FromDishka
|
from dishka import FromDishka
|
||||||
from dishka.integrations.aiogram_dialog import inject
|
from dishka.integrations.aiogram_dialog import inject
|
||||||
@@ -481,11 +481,15 @@ async def on_cancel(_callback: CallbackQuery, _button: Button, manager: DialogMa
|
|||||||
await manager.start(SharedTestsSG.tests_list, mode=StartMode.RESET_STACK)
|
await manager.start(SharedTestsSG.tests_list, mode=StartMode.RESET_STACK)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_back_to_menu(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||||
|
await manager.done()
|
||||||
|
|
||||||
|
|
||||||
shared_create_test_dialog = Dialog(
|
shared_create_test_dialog = Dialog(
|
||||||
Window(
|
Window(
|
||||||
Const("<b>📝 Создание теста</b>\n\n💬 <b>Введите название теста:</b>\n<i>(максимум 255 символов)</i>"),
|
Const("<b>📝 Создание теста</b>\n\n💬 <b>Введите название теста:</b>\n<i>(максимум 255 символов)</i>"),
|
||||||
MessageInput(on_title_input),
|
MessageInput(on_title_input),
|
||||||
Cancel(Const("◀️ Отмена")),
|
Button(Const("◀️ Отмена"), id="back", on_click=on_back_to_menu),
|
||||||
state=SharedCreateTestSG.input_title,
|
state=SharedCreateTestSG.input_title,
|
||||||
),
|
),
|
||||||
Window(
|
Window(
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class SharedTestsSG(StatesGroup):
|
|||||||
|
|
||||||
|
|
||||||
class SharedBroadcastSG(StatesGroup):
|
class SharedBroadcastSG(StatesGroup):
|
||||||
|
select_groups = State()
|
||||||
broadcast_input = State()
|
broadcast_input = State()
|
||||||
broadcast_confirm = State()
|
broadcast_confirm = State()
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ class UserDAO:
|
|||||||
models = list(result.scalars().all())
|
models = list(result.scalars().all())
|
||||||
return [UserDTO(model).to_domain() for model in models]
|
return [UserDTO(model).to_domain() for model in models]
|
||||||
|
|
||||||
|
async def get_by_groups(self, group_numbers: list[int]) -> list[DomainUser]:
|
||||||
|
result = await self.session.execute(
|
||||||
|
select(User).where(User.group.in_(group_numbers)).order_by(User.created_at.desc())
|
||||||
|
)
|
||||||
|
models = list(result.scalars().all())
|
||||||
|
return [UserDTO(model).to_domain() for model in models]
|
||||||
|
|
||||||
async def create(
|
async def create(
|
||||||
self,
|
self,
|
||||||
user_id: int,
|
user_id: int,
|
||||||
|
|||||||
@@ -22,8 +22,12 @@ class BroadcastService:
|
|||||||
bot: Bot,
|
bot: Bot,
|
||||||
message_id: int,
|
message_id: int,
|
||||||
from_chat_id: int,
|
from_chat_id: int,
|
||||||
|
group_numbers: list[int] | None = None,
|
||||||
) -> BroadcastStats:
|
) -> BroadcastStats:
|
||||||
users = await self._user_dao.get_all()
|
if group_numbers:
|
||||||
|
users = await self._user_dao.get_by_groups(group_numbers)
|
||||||
|
else:
|
||||||
|
users = await self._user_dao.get_all()
|
||||||
|
|
||||||
total = len(users)
|
total = len(users)
|
||||||
success = 0
|
success = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user