from datetime import UTC, datetime from aiogram import Bot, F, Router from aiogram.enums import ButtonStyle 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 ( 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_announcement 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, style=ButtonStyle.SUCCESS), KeyboardButton(text=CANCEL_BUTTON, style=ButtonStyle.DANGER), ] ], 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 announcement = Announcement(message=data.rich_text, datetime=datetime.now(UTC)) session.add(announcement) await session.flush() status_template = "Публикация анонса...\nОпубликовано: {}" status_msg = await msg.answer(status_template.format(0)) async for n in publish_announcement(bot, users, data.rich_text): try: await status_msg.edit_text(status_template.format(n)) except TelegramAPIError: pass 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.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)