feat: add route to upload media and edit frontend component
This commit is contained in:
@@ -7,6 +7,11 @@ from collections import defaultdict
|
||||
|
||||
from bot.utils.ttl_cache import AsyncTTLCache
|
||||
|
||||
import tempfile
|
||||
import os
|
||||
from aiogram.types import InputFile, Message
|
||||
from aiogram.exceptions import TelegramBadRequest
|
||||
|
||||
|
||||
BROADCAST_TARGET_ACTIVE_NEVER_CONNECTED = "active_never_connected"
|
||||
BROADCAST_TARGETS = {
|
||||
@@ -261,3 +266,67 @@ async def admin_broadcast_audience_counts_route(request: web.Request) -> web.Res
|
||||
)
|
||||
|
||||
return _ok({"counts": counts})
|
||||
|
||||
|
||||
async def admin_upload_media_route(request: web.Request) -> web.Response:
|
||||
"""
|
||||
Эндпоинт для загрузки медиафайла администратором.
|
||||
Принимает multipart/form-data с полем 'file'.
|
||||
Возвращает file_id, полученный от Telegram Bot.
|
||||
"""
|
||||
actor_id = _require_admin_user_id(request)
|
||||
|
||||
# Проверяем, что это multipart-запрос
|
||||
if request.content_type and not request.content_type.startswith("multipart/form-data"):
|
||||
return _error(400, "invalid_content_type")
|
||||
|
||||
reader = await request.multipart()
|
||||
field = await reader.next()
|
||||
if field is None or field.name != "file":
|
||||
return _error(400, "file_required")
|
||||
|
||||
# Сохраняем файл во временный файл
|
||||
with tempfile.NamedTemporaryFile(delete=False, suffix=".tmp") as tmp:
|
||||
while True:
|
||||
chunk = await field.read_chunk()
|
||||
if not chunk:
|
||||
break
|
||||
tmp.write(chunk)
|
||||
tmp_path = tmp.name
|
||||
|
||||
try:
|
||||
# Получаем бота из app
|
||||
bot = request.app.get("bot")
|
||||
if bot is None:
|
||||
return _error(503, "bot_unavailable")
|
||||
|
||||
# Отправляем файл в личный чат администратора, чтобы получить file_id
|
||||
# Используем send_document, так как он подходит для любых файлов
|
||||
with open(tmp_path, "rb") as f:
|
||||
input_file = InputFile(f)
|
||||
sent_msg: Message = await bot.send_document(
|
||||
chat_id=actor_id,
|
||||
document=input_file,
|
||||
# Не отправляем лишний текст, чтобы не привлекать внимание
|
||||
)
|
||||
|
||||
file_id = sent_msg.document.file_id
|
||||
|
||||
# Удаляем сообщение, чтобы не засорять чат
|
||||
await bot.delete_message(chat_id=actor_id, message_id=sent_msg.message_id)
|
||||
|
||||
# Возвращаем file_id
|
||||
return _ok({"file_id": file_id})
|
||||
|
||||
except TelegramBadRequest as e:
|
||||
logger.warning("Failed to upload media to Telegram: %s", e)
|
||||
return _error(400, f"telegram_error: {str(e)}")
|
||||
except Exception as e:
|
||||
logger.error("Unexpected error during media upload: %s", e, exc_info=True)
|
||||
return _error(500, "upload_failed")
|
||||
finally:
|
||||
# Удаляем временный файл
|
||||
try:
|
||||
os.unlink(tmp_path)
|
||||
except OSError:
|
||||
pass
|
||||
@@ -64,6 +64,8 @@ def setup_admin_routes(app: web.Application) -> None:
|
||||
|
||||
router.add_get("/api/admin/broadcast/audience-counts", admin_broadcast_audience_counts_route)
|
||||
router.add_post("/api/admin/broadcast", admin_broadcast_route)
|
||||
router.add_post("/api/admin/upload-media", admin_upload_media_route)
|
||||
|
||||
router.add_post("/api/admin/sync", admin_sync_route)
|
||||
|
||||
router.add_get("/api/admin/ads", admin_ads_list_route)
|
||||
|
||||
Reference in New Issue
Block a user