diff --git a/web/app.py b/web/app.py index 0243244..56895b0 100644 --- a/web/app.py +++ b/web/app.py @@ -87,6 +87,13 @@ async def users_page(request: Request, search: str = "", filter: str = "", usern stmt = stmt.where(User.verified == False) elif filter == 'active_unverified': stmt = stmt.where(User.verified == False, User.message_count >= 50) + elif filter == 'ig': + # Только ИГ + from database.models import InitiativeGroup + ig_stmt = select(InitiativeGroup.user_id).where(InitiativeGroup.is_active == True) + ig_result = await session.execute(ig_stmt) + ig_user_ids = [row[0] for row in ig_result.all()] + stmt = stmt.where(User.user_id.in_(ig_user_ids)) # filter == 'all' или пустой — показываем всех # Поиск @@ -114,10 +121,11 @@ async def users_page(request: Request, search: str = "", filter: str = "", usern active_unverified = (await session.execute( select(func.count(User.user_id)).where(User.verified == False, User.message_count >= 50) )).scalar() + ig_count = len(ig_user_ids) return templates.TemplateResponse("users.html", {"request": request, "username": username, "users": users, "search": search, "current_filter": filter, - "stats": {"total": total_count, "verified": verified_count, "unverified": unverified_count, "active_unverified": active_unverified}, + "stats": {"total": total_count, "verified": verified_count, "unverified": unverified_count, "active_unverified": active_unverified, "ig": ig_count}, "ig_user_ids": ig_user_ids}) @@ -592,6 +600,22 @@ async def api_send_broadcast( chat_sent = True except Exception as e: logger.error(f"Ошибка отправки в чат: {e}") + + # Логируем в базу + try: + from sqlalchemy import text + async with AsyncSessionLocal() as session: + await session.execute(text(''' + INSERT INTO activity_log (user_id, action_type, action_data, recipients_count) + VALUES (:user_id, 'broadcast', :data, :count) + '''), { + "user_id": config.ADMIN_USER_ID, + "data": f"recipients={recipients}, text_len={len(text)}", + "count": success_count + }) + await session.commit() + except Exception as e: + logger.error(f"Ошибка логирования рассылки: {e}") return JSONResponse({ "success": True, @@ -605,6 +629,44 @@ async def api_send_broadcast( }) +# ============================================================================ +# ЛОГИРОВАНИЕ АКТИВНОСТИ +# ============================================================================ + +@app.get("/api/activity_log") +async def api_get_activity_log( + username: str = Depends(get_current_admin), + action_type: str = "", + days: int = 30 +): + """Получить логи активности""" + from sqlalchemy import text, desc + + async with AsyncSessionLocal() as session: + stmt = text(''' + SELECT * FROM activity_log + WHERE (:action_type = '' OR action_type = :action_type) + AND created_at >= datetime('now', '-' || :days || ' days') + ORDER BY created_at DESC + LIMIT 100 + ''') + + result = await session.execute(stmt, {"action_type": action_type, "days": str(days)}) + logs = [ + { + "id": row[0], + "user_id": row[1], + "action_type": row[2], + "action_data": row[3], + "recipients_count": row[4], + "created_at": row[5] + } + for row in result.all() + ] + + return JSONResponse({"success": True, "logs": logs}) + + # ============================================================================ # ОПРОСЫ # ============================================================================ @@ -744,22 +806,9 @@ async def api_export_json(username: str = Depends(get_current_admin)): # ============================================================================ -# НАСТРОЙКИ +# НАСТРОЙКИ (перенесено в /broadcast) # ============================================================================ - -@app.get("/settings", response_class=HTMLResponse) -async def settings_page(request: Request, username: str = Depends(get_current_admin)): - """Страница настроек""" - async with AsyncSessionLocal() as session: - from sqlalchemy import text - result = await session.execute(text("SELECT key, value FROM settings")) - settings = {row[0]: row[1] for row in result.all()} - - return templates.TemplateResponse("settings.html", { - "request": request, - "username": username, - "settings": settings - }) +# Настройка лимита рассылки ИГ теперь в /broadcast @app.post("/api/settings/ig_broadcast_limit") @@ -781,19 +830,24 @@ async def api_set_ig_broadcast_limit( return JSONResponse({"success": True, "message": f"Лимит установлен: {limit} сек"}) -@app.get("/settings", response_class=HTMLResponse) -async def settings_page(request: Request, username: str = Depends(get_current_admin)): - """Настройки""" - return templates.TemplateResponse("settings.html", { - "request": request, "username": username, - "config": { - "bot_token": config.BOT_TOKEN[:20] + "...", - "admin_chat_id": config.ADMIN_CHAT_ID, - "admin_user_id": config.ADMIN_USER_ID, - "use_proxy": config.USE_PROXY, - "web_login": config.WEB_ADMIN_LOGIN - } - }) + +@app.get("/api/settings/ig_broadcast_limit") +async def api_get_ig_broadcast_limit( + username: str = Depends(get_current_admin) +): + """Получить лимит рассылки ИГ""" + from sqlalchemy import text + + async with AsyncSessionLocal() as session: + result = await session.execute(text(''' + SELECT value FROM settings WHERE key = 'ig_broadcast_limit' + ''')) + row = result.first() + + if row: + return JSONResponse({"success": True, "value": row[0]}) + else: + return JSONResponse({"success": True, "value": "0"}) def run_web_server(host: str = "0.0.0.0", port: int = 8000): diff --git a/web/templates/broadcast.html b/web/templates/broadcast.html index 799e8cb..87fa338 100644 --- a/web/templates/broadcast.html +++ b/web/templates/broadcast.html @@ -91,17 +91,85 @@
  1. Введите текст объявления
  2. Прикрепите картинку (необязательно)
  3. +
  4. Выберите получателей
  5. Нажмите "Отправить рассылку"
  6. -
  7. Бот отправит всем верифицированным пользователям в личку
  8. -
  9. Также отправит в общий чат
  10. +
  11. Бот отправит выбранным получателям
+ + +
+
+
Настройка рассылки ИГ
+
+
+
+
+ + + +
    +
  • 0 — Без ограничений
  • +
  • 3600 — 1 раз в час
  • +
  • 86400 — 1 раз в сутки
  • +
+
+
+ +
+
+
+
- - - diff --git a/web/templates/users.html b/web/templates/users.html index b5b7550..e695d59 100644 --- a/web/templates/users.html +++ b/web/templates/users.html @@ -42,6 +42,8 @@ ✅ Верифицированные {% elif current_filter == 'unverified' %} ⏳ Не верифицированы + {% elif current_filter == 'ig' %} + 🔵 Инициативная группа {% elif current_filter == 'active_unverified' %} 🔥 Активные (50+ сообщ.) {% else %} @@ -78,11 +80,11 @@
- -
+ +
-

{{ stats.unverified }}

- Не верифицировано +

{{ stats.ig }}

+ Инициативная группа