diff options
Diffstat (limited to 'handlers')
| -rw-r--r-- | handlers/admin/__init__.py | 8 | ||||
| -rw-r--r-- | handlers/admin/new_announcement.py | 108 |
2 files changed, 116 insertions, 0 deletions
diff --git a/handlers/admin/__init__.py b/handlers/admin/__init__.py index d0a5587..2f7c74f 100644 --- a/handlers/admin/__init__.py +++ b/handlers/admin/__init__.py | |||
| @@ -1,6 +1,14 @@ | |||
| 1 | from aiogram import F, Router | 1 | from aiogram import F, Router |
| 2 | from aiogram.filters import MagicData | 2 | from aiogram.filters import MagicData |
| 3 | 3 | ||
| 4 | # isort: off | ||
| 5 | from . import new_announcement | ||
| 6 | # isort: on | ||
| 7 | |||
| 4 | router = Router(name="admin") | 8 | router = Router(name="admin") |
| 5 | router.message.filter(MagicData(F.user.is_admin())) | 9 | router.message.filter(MagicData(F.user.is_admin())) |
| 6 | router.callback_query.filter(MagicData(F.user.is_admin())) | 10 | router.callback_query.filter(MagicData(F.user.is_admin())) |
| 11 | |||
| 12 | router.include_routers( | ||
| 13 | new_announcement.router, | ||
| 14 | ) | ||
diff --git a/handlers/admin/new_announcement.py b/handlers/admin/new_announcement.py new file mode 100644 index 0000000..79cf8d4 --- /dev/null +++ b/handlers/admin/new_announcement.py | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | from datetime import UTC, datetime | ||
| 2 | |||
| 3 | from aiogram import Bot, F, Router | ||
| 4 | from aiogram.exceptions import TelegramAPIError | ||
| 5 | from aiogram.filters import Command | ||
| 6 | from aiogram.fsm.context import FSMContext | ||
| 7 | from aiogram.fsm.state import State, StatesGroup | ||
| 8 | from aiogram.types import ( | ||
| 9 | ContentType, | ||
| 10 | KeyboardButton, | ||
| 11 | Message, | ||
| 12 | ReplyKeyboardMarkup, | ||
| 13 | ReplyKeyboardRemove, | ||
| 14 | ) | ||
| 15 | from pydantic.main import BaseModel | ||
| 16 | from sqlalchemy import select | ||
| 17 | from sqlalchemy.ext.asyncio import AsyncSession | ||
| 18 | |||
| 19 | from libs.fsm import get_data, set_data | ||
| 20 | from libs.msg import publish | ||
| 21 | from models import Announcement, RichText, User | ||
| 22 | |||
| 23 | router = Router(name="new_announcement") | ||
| 24 | |||
| 25 | |||
| 26 | class NewAnnouncementStates(StatesGroup): | ||
| 27 | message = State() | ||
| 28 | |||
| 29 | |||
| 30 | class NewAnnouncementData(BaseModel): | ||
| 31 | rich_text: RichText | None = None | ||
| 32 | |||
| 33 | |||
| 34 | SEND_BUTTON = "Опубликовать" | ||
| 35 | CANCEL_BUTTON = "Отменить создание" | ||
| 36 | |||
| 37 | |||
| 38 | @router.message(Command("new_announcement")) | ||
| 39 | async def new_announcement_command(msg: Message, state: FSMContext) -> None: | ||
| 40 | await msg.answer( | ||
| 41 | "Укажите сообщение для анонса.", | ||
| 42 | reply_markup=ReplyKeyboardMarkup( | ||
| 43 | keyboard=[ | ||
| 44 | [ | ||
| 45 | KeyboardButton(text=SEND_BUTTON), | ||
| 46 | KeyboardButton(text=CANCEL_BUTTON), | ||
| 47 | ] | ||
| 48 | ], | ||
| 49 | resize_keyboard=True, | ||
| 50 | ), | ||
| 51 | ) | ||
| 52 | await state.set_state(NewAnnouncementStates.message) | ||
| 53 | |||
| 54 | |||
| 55 | @router.message(NewAnnouncementStates.message, F.text == SEND_BUTTON) | ||
| 56 | async def announcement_send( | ||
| 57 | msg: Message, | ||
| 58 | bot: Bot, | ||
| 59 | state: FSMContext, | ||
| 60 | session: AsyncSession, | ||
| 61 | ) -> None: | ||
| 62 | users = await session.scalars(select(User.id).where(User.id != msg.chat.id)) | ||
| 63 | data = await get_data(state, NewAnnouncementData) | ||
| 64 | |||
| 65 | if data.rich_text is None: | ||
| 66 | await msg.answer("Для публикации анонса укажите текст сообщения.") | ||
| 67 | return | ||
| 68 | |||
| 69 | status_template = "Публикация анонса...\nОпубликовано: {}" | ||
| 70 | status_msg = await msg.answer(status_template.format(0)) | ||
| 71 | |||
| 72 | async for n in publish(bot, users, data.rich_text): | ||
| 73 | try: | ||
| 74 | await status_msg.edit_text(status_template.format(n)) | ||
| 75 | except TelegramAPIError: | ||
| 76 | pass | ||
| 77 | |||
| 78 | announcement = Announcement(message=data, datetime=datetime.now(UTC)) | ||
| 79 | session.add(announcement) | ||
| 80 | |||
| 81 | await status_msg.delete() | ||
| 82 | await msg.answer( | ||
| 83 | "Анонс отправлен всем пользователям", | ||
| 84 | reply_markup=ReplyKeyboardRemove(), | ||
| 85 | ) | ||
| 86 | await state.clear() | ||
| 87 | |||
| 88 | |||
| 89 | @router.message(NewAnnouncementStates.message, F.text == CANCEL_BUTTON) | ||
| 90 | async def announcement_cancel(msg: Message, state: FSMContext) -> None: | ||
| 91 | await msg.answer("Создание анонса отменено", reply_markup=ReplyKeyboardRemove()) | ||
| 92 | await state.clear() | ||
| 93 | |||
| 94 | |||
| 95 | @router.message(NewAnnouncementStates.message) | ||
| 96 | async def announcement_message(msg: Message, bot: Bot, state: FSMContext) -> None: | ||
| 97 | if msg.content_type != ContentType.TEXT or msg.text is None: | ||
| 98 | await msg.answer( | ||
| 99 | "Неверный тип сообщения.\n" | ||
| 100 | "Бот поддерживает отправку только текстовых анонсов." | ||
| 101 | ) | ||
| 102 | return | ||
| 103 | |||
| 104 | rich_text = RichText.from_message(msg) | ||
| 105 | await set_data(state, NewAnnouncementData(rich_text=rich_text)) | ||
| 106 | |||
| 107 | msg_rich_text = RichText.from_text("Сообщение вашего анонса:\n", rich_text) | ||
| 108 | await msg_rich_text.send(bot, msg.chat.id) | ||
