From f7b7e87cffc9dcb2817b070d7a003ac234c96ec3 Mon Sep 17 00:00:00 2001 From: Tolmachev Igor Date: Mon, 23 Mar 2026 18:07:30 +0300 Subject: Add new_announcement command --- handlers/admin/new_announcement.py | 108 +++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 handlers/admin/new_announcement.py (limited to 'handlers/admin/new_announcement.py') 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 @@ +from datetime import UTC, datetime + +from aiogram import Bot, F, Router +from aiogram.exceptions import TelegramAPIError +from aiogram.filters import Command +from aiogram.fsm.context import FSMContext +from aiogram.fsm.state import State, StatesGroup +from aiogram.types import ( + ContentType, + KeyboardButton, + Message, + ReplyKeyboardMarkup, + ReplyKeyboardRemove, +) +from pydantic.main import BaseModel +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from libs.fsm import get_data, set_data +from libs.msg import publish +from models import Announcement, RichText, User + +router = Router(name="new_announcement") + + +class NewAnnouncementStates(StatesGroup): + message = State() + + +class NewAnnouncementData(BaseModel): + rich_text: RichText | None = None + + +SEND_BUTTON = "Опубликовать" +CANCEL_BUTTON = "Отменить создание" + + +@router.message(Command("new_announcement")) +async def new_announcement_command(msg: Message, state: FSMContext) -> None: + await msg.answer( + "Укажите сообщение для анонса.", + reply_markup=ReplyKeyboardMarkup( + keyboard=[ + [ + KeyboardButton(text=SEND_BUTTON), + KeyboardButton(text=CANCEL_BUTTON), + ] + ], + resize_keyboard=True, + ), + ) + await state.set_state(NewAnnouncementStates.message) + + +@router.message(NewAnnouncementStates.message, F.text == SEND_BUTTON) +async def announcement_send( + msg: Message, + bot: Bot, + state: FSMContext, + session: AsyncSession, +) -> None: + users = await session.scalars(select(User.id).where(User.id != msg.chat.id)) + data = await get_data(state, NewAnnouncementData) + + if data.rich_text is None: + await msg.answer("Для публикации анонса укажите текст сообщения.") + return + + status_template = "Публикация анонса...\nОпубликовано: {}" + status_msg = await msg.answer(status_template.format(0)) + + async for n in publish(bot, users, data.rich_text): + try: + await status_msg.edit_text(status_template.format(n)) + except TelegramAPIError: + pass + + announcement = Announcement(message=data, datetime=datetime.now(UTC)) + session.add(announcement) + + await status_msg.delete() + await msg.answer( + "Анонс отправлен всем пользователям", + reply_markup=ReplyKeyboardRemove(), + ) + await state.clear() + + +@router.message(NewAnnouncementStates.message, F.text == CANCEL_BUTTON) +async def announcement_cancel(msg: Message, state: FSMContext) -> None: + await msg.answer("Создание анонса отменено", reply_markup=ReplyKeyboardRemove()) + await state.clear() + + +@router.message(NewAnnouncementStates.message) +async def announcement_message(msg: Message, bot: Bot, state: FSMContext) -> None: + if msg.content_type != ContentType.TEXT or msg.text is None: + await msg.answer( + "Неверный тип сообщения.\n" + "Бот поддерживает отправку только текстовых анонсов." + ) + return + + rich_text = RichText.from_message(msg) + await set_data(state, NewAnnouncementData(rich_text=rich_text)) + + msg_rich_text = RichText.from_text("Сообщение вашего анонса:\n", rich_text) + await msg_rich_text.send(bot, msg.chat.id) -- cgit v1.3