diff --git a/web/app.py b/web/app.py index f1b47a3..895d284 100644 --- a/web/app.py +++ b/web/app.py @@ -1146,6 +1146,98 @@ async def api_send_broadcast_reminder(broadcast_id: int, username: str = Depends }) +@app.get("/api/broadcasts/list") +async def api_get_broadcasts_list(limit: int = 10, username: str = Depends(get_current_admin)): + """Получить список последних рассылок""" + from database.models import Broadcast, BroadcastRead + from sqlalchemy import func, desc + + async with AsyncSessionLocal() as session: + stmt = select(Broadcast).order_by(Broadcast.created_at.desc()).limit(limit) + result = await session.execute(stmt) + broadcasts = list(result.scalars().all()) + + total = (await session.execute(select(func.count(Broadcast.id)))).scalar() or 0 + + broadcasts_data = [] + for b in broadcasts: + read_count = (await session.execute( + select(func.count(BroadcastRead.id)).where(BroadcastRead.broadcast_id == b.id) + )).scalar() or 0 + + broadcasts_data.append({ + 'id': b.id, + 'text': b.text, + 'sent_at': b.sent_at.strftime('%d.%m.%Y %H:%M') if b.sent_at else '-', + 'total_sent': b.total_sent, + 'read_count': read_count, + 'read_percent': round((read_count / b.total_sent * 100), 1) if b.total_sent > 0 else 0, + 'broadcast_type': b.broadcast_type, + }) + + return JSONResponse({ + "success": True, + "total": total, + "broadcasts": broadcasts_data + }) + + +@app.post("/api/broadcast/create") +async def api_create_broadcast( + text: str = Form(...), + photo: UploadFile = File(None), + recipients: str = Form("all_and_chat"), + has_read_button: bool = Form(True), + username: str = Depends(get_current_admin) +): + """Создать и отправить рассылку из веб-панели""" + import aiohttp + from database.models import Broadcast + from datetime import datetime + + photo_file_id = None + + # Если есть фото - загружаем + if photo and photo.filename: + try: + temp_path = Path("data") / f"broadcast_{photo.filename}" + with open(temp_path, "wb") as f: + content = await photo.read() + f.write(content) + + async with aiohttp.ClientSession() as session: + url = f"https://api.telegram.org/bot{config.BOT_TOKEN}/sendPhoto" + data = aiohttp.FormData() + data.add_field('chat_id', config.ADMIN_USER_ID) + data.add_field('photo', open(temp_path, 'rb'), filename=photo.filename) + data.add_field('caption', 'Test') + + async with session.post(url, data=data) as resp: + result = await resp.json() + if result.get('ok'): + photo_file_id = result['result']['photo'][-1]['file_id'] + except Exception as e: + logger.error(f"Ошибка загрузки фото: {e}") + + # Получаем бота + import main + bot = main.bot + + # Отправляем рассылку + from handlers.smart_broadcast import send_smart_broadcast + success = await send_smart_broadcast(bot, text, recipients, photo_file_id) + + if success: + return JSONResponse({ + "success": True, + "message": f"Рассылка отправлена получателям: {recipients}" + }) + else: + return JSONResponse({ + "error": "Ошибка при отправке рассылки" + }, status_code=500) + + @app.get("/api/broadcast/readers/{broadcast_id}") async def api_get_broadcast_readers(broadcast_id: int, username: str = Depends(get_current_admin)): """Получить список прочитавших""" diff --git a/web/templates/broadcast.html b/web/templates/broadcast.html index f84a18c..dcde834 100644 --- a/web/templates/broadcast.html +++ b/web/templates/broadcast.html @@ -1,252 +1,320 @@ - - -
- -