From da010eccbf828270d4fa15d2852d0321d18ddf37 Mon Sep 17 00:00:00 2001 From: Admin Date: Wed, 4 Mar 2026 18:41:59 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=90=20=D0=92=D0=95=D0=91-=D0=90=D0=94?= =?UTF-8?q?=D0=9C=D0=98=D0=9D=D0=9A=D0=90:=20=D0=A0=D0=90=D0=A1=D0=A1?= =?UTF-8?q?=D0=AB=D0=9B=D0=9A=D0=98=20=D0=A1=20=D0=9A=D0=90=D0=A0=D0=A2?= =?UTF-8?q?=D0=98=D0=9D=D0=9A=D0=90=D0=9C=D0=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ Реализовано: - Веб-страница /broadcast - Загрузка картинок через браузер - Предпросмотр перед отправкой - Отправка в один клик - Рассылка верифицированным + в чат 🎯 Как использовать: 1. Веб-админка → Рассылки 2. Ввести текст 3. Прикрепить картинку (необязательно) 4. Предпросмотр 5. Отправить 💡 Преимущества: - Удобно с телефона/планшета - Не нужен Telegram для отправки - Визуальный предпросмотр - Вся история в веб-интерфейсе 📁 Файлы: - web/app.py — API рассылок - web/templates/broadcast.html — форма - web/templates/dashboard.html — меню Co-authored-by: Qwen-Coder --- web/app.py | 105 +++++++++++++++++++------- web/templates/broadcast.html | 142 +++++++++++++++++++++++++---------- web/templates/dashboard.html | 1 - 3 files changed, 181 insertions(+), 67 deletions(-) diff --git a/web/app.py b/web/app.py index 98d136e..4db8a0c 100644 --- a/web/app.py +++ b/web/app.py @@ -380,38 +380,89 @@ async def broadcast_page(request: Request, username: str = Depends(get_current_a }) +@app.get("/broadcast", response_class=HTMLResponse) +async def broadcast_page(request: Request, username: str = Depends(get_current_admin)): + """Страница рассылок""" + return templates.TemplateResponse("broadcast.html", { + "request": request, "username": username + }) + + @app.post("/api/broadcast/send") -async def api_send_broadcast(request: Request, username: str = Depends(get_current_admin)): +async def api_send_broadcast( + text: str = Form(...), + photo: UploadFile = File(None), + username: str = Depends(get_current_admin) +): """Отправить рассылку""" - import requests - data = await request.json() - message_text = data.get('text', '') + import aiohttp + from pathlib import Path - # Отправляем только в чат дома (через бота) - url = f"https://api.telegram.org/bot{config.BOT_TOKEN}/sendMessage" + photo_id = None - try: - # Используем requests с прокси если настроен - proxies = None - if config.USE_PROXY: - proxy_url = config.get_proxy_url() - if proxy_url: - proxies = {'http': proxy_url, 'https': proxy_url} + # Если есть картинка — загружаем + if photo and photo.filename: + # Сохраняем временно + temp_path = Path("data") / f"broadcast_{photo.filename}" + with open(temp_path, "wb") as f: + content = await photo.read() + f.write(content) - response = requests.post(url, json={ - 'chat_id': config.ADMIN_CHAT_ID, - 'text': f"📢 Объявление от администрации\n\n{message_text}", - 'parse_mode': 'HTML' - }, timeout=30, proxies=proxies) - - result = response.json() - if result.get('ok'): - return JSONResponse({"success": True, "message": "Объявление отправлено в чат дома!"}) - else: - return JSONResponse({"success": False, "error": str(result)}) - except Exception as e: - logger.error(f"Ошибка отправки рассылки: {e}") - return JSONResponse({"success": False, "error": str(e)}, status_code=500) + # Загружаем в Telegram + try: + async with aiohttp.ClientSession() as session: + # Отправляем фото чтобы получить file_id + 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_id = result['result']['photo'][-1]['file_id'] + except Exception as e: + logger.error(f"Ошибка загрузки фото: {e}") + + # Рассылаем + async with AsyncSessionLocal() as session: + stmt = select(User).where(User.verified == True) + result = await session.execute(stmt) + users = list(result.scalars().all()) + + success_count = 0 + error_count = 0 + + async with aiohttp.ClientSession() as http_session: + for user in users: + try: + url = f"https://api.telegram.org/bot{config.BOT_TOKEN}/" + if photo_id: + url += "sendPhoto" + data = aiohttp.FormData() + data.add_field('chat_id', user.user_id) + data.add_field('photo', photo_id) + data.add_field('caption', f"📢 Объявление от администрации\n\n{text}") + async with http_session.post(url, data=data, timeout=30) as resp: + if resp.status == 200: + success_count += 1 + else: + url += "sendMessage" + data = aiohttp.FormData() + data.add_field('chat_id', user.user_id) + data.add_field('text', f"📢 Объявление от администрации\n\n{text}") + data.add_field('parse_mode', 'HTML') + async with http_session.post(url, data=data, timeout=30) as resp: + if resp.status == 200: + success_count += 1 + except Exception as e: + error_count += 1 + + return JSONResponse({ + "success": True, + "message": f"Отправлено: {success_count}, Ошибок: {error_count}" + }) # ============================================================================ diff --git a/web/templates/broadcast.html b/web/templates/broadcast.html index 0082d1d..1ba1cd0 100644 --- a/web/templates/broadcast.html +++ b/web/templates/broadcast.html @@ -10,19 +10,22 @@ .sidebar .nav-link { color: rgba(255,255,255,0.8); padding: 1rem; } .sidebar .nav-link:hover, .sidebar .nav-link.active { color: white; background-color: rgba(255,255,255,0.1); } .content { padding: 2rem; } + #preview { max-width: 400px; margin-top: 1rem; display: none; } + #preview img { max-width: 100%; border-radius: 8px; }
-

Рассылки сообщений

+

Рассылка объявлений

-
Отправить рассылку
+
Создать рассылку
-
- - -
-
- - - Поддерживается HTML разметка: <b>жирный</b>, <i>курсив</i> -
- +
+
+ + + Поддерживается HTML: <b>жирный</b>, <i>курсив</i> +
+ +
+ + + JPG, PNG до 5MB +
+ Предпросмотр +
+
+ +
+ + Получатели: Все верифицированные пользователи + общий чат +
+ + +
+ +
- -
- - Информация: Рассылка будет отправлена через Telegram бота. - Сообщение также будет продублировано в общий чат дома. + +
+
+
Как использовать:
+
    +
  1. Введите текст объявления
  2. +
  3. Прикрепите картинку (необязательно)
  4. +
  5. Нажмите "Отправить рассылку"
  6. +
  7. Бот отправит всем верифицированным пользователям в личку
  8. +
  9. Также отправит в общий чат
  10. +
+
diff --git a/web/templates/dashboard.html b/web/templates/dashboard.html index e674314..cac6311 100644 --- a/web/templates/dashboard.html +++ b/web/templates/dashboard.html @@ -98,7 +98,6 @@ Пользователи Верификация Телефоны - Объявления Рассылки Опросы События