- Add `/push` command for admins to broadcast messages to all users with approve/decline confirmation flow (handlers/ads.py) - Add common handler for undefined commands and delete callback - Add `UserRepository.get_users()` to fetch all users - Add `UserService.send_promotion_to_users()` with error logging - Add promotion confirmation keyboard and FSM state - Add PostgreSQL availability check in startup.sh before migrations - Add new text constants for promotion flow
70 lines
2.2 KiB
Python
70 lines
2.2 KiB
Python
from aiogram import F, Router
|
|
from aiogram.filters import Command
|
|
from aiogram.fsm.context import FSMContext
|
|
from aiogram.types import CallbackQuery, Message
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from config import settings
|
|
from keyboards.admins import promotion_confirmation
|
|
from keyboards.client import build_return_menu
|
|
from schemas.di import DependenciesDTO
|
|
from schemas.promotions import NewPromotionContext
|
|
from states.admins import AdminStorage
|
|
from texts import (
|
|
ADMIN_PROMOTION_CONFIRMATION,
|
|
ADMIN_PROMOTION_INIT,
|
|
ADMIN_STANDARD_FALLBACK,
|
|
SUCCESSFULLY_SENT_PROMOTION,
|
|
)
|
|
|
|
router = Router()
|
|
|
|
|
|
@router.message(
|
|
F.chat.id == settings.admin_group_id, F.message_thread_id.is_(None), Command("push")
|
|
)
|
|
async def push_cmd(msg: Message, state: FSMContext):
|
|
await state.clear()
|
|
|
|
ctx = NewPromotionContext(creator_id=msg.from_user.id)
|
|
await msg.delete()
|
|
await msg.answer(ADMIN_PROMOTION_INIT, reply_markup=build_return_menu("delete", "❌"))
|
|
|
|
await state.set_state(AdminStorage.promotion_msg)
|
|
await state.set_data({"ctx": ctx})
|
|
|
|
|
|
@router.message(F.chat.id == settings.admin_group_id, AdminStorage.promotion_msg)
|
|
async def send_promotion(msg: Message, state: FSMContext):
|
|
data = await state.get_data()
|
|
|
|
ctx: NewPromotionContext | None = data.get("ctx")
|
|
if not ctx:
|
|
await msg.answer(ADMIN_STANDARD_FALLBACK)
|
|
return
|
|
|
|
ctx.message = msg
|
|
await msg.reply(ADMIN_PROMOTION_CONFIRMATION, reply_markup=promotion_confirmation)
|
|
|
|
await state.set_state(AdminStorage.promotion_msg)
|
|
await state.set_data({"ctx": ctx})
|
|
|
|
|
|
@router.callback_query(AdminStorage.promotion_msg, F.data.in_(["approve", "decline"]))
|
|
async def promotion_action(
|
|
cb: CallbackQuery, state: FSMContext, session: AsyncSession, deps: DependenciesDTO
|
|
):
|
|
data = await state.get_data()
|
|
await state.clear()
|
|
|
|
await cb.message.delete()
|
|
if cb.data == "decline":
|
|
return
|
|
ctx: NewPromotionContext | None = data.get("ctx")
|
|
if not (ctx and ctx.creator_id and ctx.message):
|
|
await cb.message.answer(ADMIN_STANDARD_FALLBACK)
|
|
return
|
|
|
|
sent = await deps.user_service.send_promotion_to_users(session, ctx.message)
|
|
await cb.message.answer(SUCCESSFULLY_SENT_PROMOTION.format(user_count=len(sent)))
|