diff --git a/README.md b/README.md index 22574ca..114f415 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ - [ ] Удалять мероприятия - [x] Просматривать список существующих мероприятий - [ ] Просматривать людей записавшихся на мероприятие +- [x] Делать рассылку по пользователям + - [x] Исключать из рассылки администраторов + - [ ] Указать дату и время рассылки --- diff --git a/app/bot/dialogs/flows/__init__.py b/app/bot/dialogs/flows/__init__.py index c501662..0f694b7 100644 --- a/app/bot/dialogs/flows/__init__.py +++ b/app/bot/dialogs/flows/__init__.py @@ -1,6 +1,7 @@ from aiogram import Router from .events.dialogs import events_dialog +from .mailing.dialogs import mailing_dialog from .new_event.dialogs import new_event_dialog from .profile.dialogs import profile_dialog from .start.dialogs import start_dialog @@ -10,3 +11,4 @@ dialogs_router.include_router(start_dialog) dialogs_router.include_router(events_dialog) dialogs_router.include_router(profile_dialog) dialogs_router.include_router(new_event_dialog) +dialogs_router.include_router(mailing_dialog) diff --git a/app/bot/dialogs/flows/mailing/dialogs.py b/app/bot/dialogs/flows/mailing/dialogs.py new file mode 100644 index 0000000..b33feeb --- /dev/null +++ b/app/bot/dialogs/flows/mailing/dialogs.py @@ -0,0 +1,43 @@ +from aiogram_dialog import Dialog, Window +from aiogram_dialog.widgets.input import MessageInput +from aiogram_dialog.widgets.kbd import Back, Button, Cancel +from aiogram_dialog.widgets.text import Const + +# from .getters import event_getter, events_list_getter, registration_getter +from .handlers import choose_recipients, confirm_mailing, message_data +from .states import MailingSG + +mailing_dialog = Dialog( + Window( + Const("Пришлите сообщение которое хотите разослать"), + MessageInput(message_data), + Cancel(Const("Отмена")), + state=MailingSG.message_data, + ), + Window( + Const("Кому отправить сообщение?"), + Button( + Const("Всем"), + id="send_all", + on_click=choose_recipients, + ), + Button( + Const("Не админам"), + id="send_users", + on_click=choose_recipients, + ), + Back(Const("Назад")), + state=MailingSG.recipients, + ), + Window( + Const("Начать рассылку?"), + Button( + Const("Разослать"), + id="start_mailing", + on_click=confirm_mailing, + ), + Back(Const("Назад")), + Cancel(Const("Отмена")), + state=MailingSG.confirm, + ), +) diff --git a/app/bot/dialogs/flows/mailing/handlers.py b/app/bot/dialogs/flows/mailing/handlers.py new file mode 100644 index 0000000..4606e12 --- /dev/null +++ b/app/bot/dialogs/flows/mailing/handlers.py @@ -0,0 +1,54 @@ +import asyncio + +from aiogram.types import CallbackQuery, Message +from aiogram_dialog import DialogManager +from aiogram_dialog.widgets.input import MessageInput +from aiogram_dialog.widgets.kbd import Button + +from app.infrastructure.database.crud import get_users + + +async def message_data( + message: Message, + widget: MessageInput, + manager: DialogManager, +): + manager.dialog_data["message"] = message + await manager.next() + + +async def choose_recipients( + callback: CallbackQuery, + button: Button, + manager: DialogManager, +): + manager.dialog_data["recipients"] = button.widget_id + await manager.next() + + +async def confirm_mailing( + callback: CallbackQuery, + button: Button, + manager: DialogManager, +): + print(manager.dialog_data["recipients"]) + users = list(await get_users( + manager.middleware_data["session"], + exclude_admins=manager.dialog_data["recipients"] == "send_users", + )) + + await asyncio.gather( + *[ + callback.bot.copy_message( + chat_id=user.tg_id, + from_chat_id=manager.dialog_data["message"].chat.id, + message_id=manager.dialog_data["message"].message_id, + ) + for user in users + ] + ) + await callback.bot.send_message( + chat_id=callback.from_user.id, + text=f"Разослано сообщений: {len(users)}", + ) + await manager.done() diff --git a/app/bot/dialogs/flows/mailing/states.py b/app/bot/dialogs/flows/mailing/states.py new file mode 100644 index 0000000..0b3ec5d --- /dev/null +++ b/app/bot/dialogs/flows/mailing/states.py @@ -0,0 +1,8 @@ +from aiogram.fsm.state import State, StatesGroup + + +class MailingSG(StatesGroup): + message_data = State() + recipients = State() + # mailing_date = State() + confirm = State() diff --git a/app/bot/dialogs/flows/start/dialogs.py b/app/bot/dialogs/flows/start/dialogs.py index 3abf0fe..a82fa59 100644 --- a/app/bot/dialogs/flows/start/dialogs.py +++ b/app/bot/dialogs/flows/start/dialogs.py @@ -3,6 +3,7 @@ from aiogram_dialog.widgets.kbd import Button, Start from aiogram_dialog.widgets.text import Const, Format from app.bot.dialogs.flows.events.dialogs import EventsSG +from app.bot.dialogs.flows.mailing.dialogs import MailingSG from app.bot.dialogs.flows.new_event.dialogs import NewEventSG from app.bot.dialogs.flows.profile.dialogs import ProfileSG from app.bot.dialogs.widgets.getters import is_admin_getter, user_getter @@ -19,6 +20,12 @@ start_dialog = Dialog( state=NewEventSG.input_title, when="is_admin", ), + Start( + Const("📧 разослать сообщение"), + id="create_mailing", + state=MailingSG.message_data, + when="is_admin", + ), Start(Const("👤 профиль"), id="profile", state=ProfileSG.profile), getter=[user_getter, is_admin_getter], state=StartSG.start, diff --git a/app/infrastructure/database/crud.py b/app/infrastructure/database/crud.py index f7f07a3..f09091d 100644 --- a/app/infrastructure/database/crud.py +++ b/app/infrastructure/database/crud.py @@ -41,6 +41,11 @@ async def update_user( return user +async def get_users(session: AsyncSession, exclude_admins=False) -> list[User]: + query = select(User).where(User.role == "user") if exclude_admins else select(User) + return await session.scalars(query) + + async def get_events_list(session: AsyncSession) -> list[Event]: result = await session.execute( select(Event).where(Event.start_date >= datetime.now())