Files
Nexus/server/application/services/offline_daily_report_schedule.py
T
r 9c9a51263c chore: 本地快照 — 终端 UI、服务器 site_url、后台任务与 Agent 导航
汇总未提交工作区:Servers 列表 site_url 链接、终端页与选机器 UI、SSH 连通性与 Telegram 通知、agent/offline/unset_path 后台调度与 gate_ai_review 门控;更新 AGENTS 与 agent-exploration 规则(仅本机 commit);同步 web/app 构建产物与相关文档。
2026-06-18 06:01:50 +08:00

97 lines
3.1 KiB
Python

"""Scheduled daily offline-server count report to Telegram."""
from __future__ import annotations
import logging
from datetime import datetime, timezone
from typing import Any
from server.application.server_connectivity import scan_monitored_connectivity
from server.config import settings
from server.infrastructure.database.session import AsyncSessionLocal
from server.infrastructure.redis.client import get_redis
from server.utils.display_time import to_beijing
logger = logging.getLogger("nexus.offline_daily_report_schedule")
REDIS_LAST_RUN_KEY = "nexus:offline_daily_report:last_run_date"
def _enabled() -> bool:
return str(getattr(settings, "OFFLINE_DAILY_REPORT_ENABLED", "true")).lower() == "true"
def _cron_expr() -> str:
expr = (getattr(settings, "OFFLINE_DAILY_REPORT_CRON", None) or "50 11 * * *").strip()
return expr or "50 11 * * *"
async def _mark_ran_today() -> None:
today = to_beijing(datetime.now(timezone.utc)).strftime("%Y-%m-%d")
redis = get_redis()
await redis.set(REDIS_LAST_RUN_KEY, today, ex=86400 * 2)
async def _already_ran_today() -> bool:
today = to_beijing(datetime.now(timezone.utc)).strftime("%Y-%m-%d")
redis = get_redis()
last = await redis.get(REDIS_LAST_RUN_KEY)
if last is None:
return False
if isinstance(last, bytes):
last = last.decode()
return str(last).strip() == today
async def collect_offline_snapshot() -> dict[str, Any]:
"""Scan Agent-monitored servers and return online/offline counts + offline list."""
redis = get_redis()
async with AsyncSessionLocal() as session:
return await scan_monitored_connectivity(redis, session)
async def run_scheduled_offline_daily_report(*, force: bool = False) -> dict[str, Any]:
"""Send Telegram summary of offline server count; once per Beijing calendar day."""
if not _enabled() and not force:
return {"action": "skipped", "reason": "disabled"}
if not force and await _already_ran_today():
return {"action": "skipped", "reason": "already_ran_today"}
snapshot = await collect_offline_snapshot()
monitored = int(snapshot.get("monitored") or 0)
if monitored == 0:
await _mark_ran_today()
return {"action": "skipped", "reason": "no_monitored_servers"}
from server.infrastructure.telegram import send_telegram_offline_daily_report
sent = await send_telegram_offline_daily_report(
monitored=monitored,
online=int(snapshot.get("online") or 0),
offline=int(snapshot.get("offline") or 0),
offline_servers=list(snapshot.get("offline_servers") or []),
)
await _mark_ran_today()
if not sent:
return {
"action": "skipped",
"reason": "telegram_not_sent",
"monitored": monitored,
"offline": snapshot.get("offline"),
}
logger.info(
"Offline daily report sent: monitored=%s online=%s offline=%s",
monitored,
snapshot.get("online"),
snapshot.get("offline"),
)
return {
"action": "sent",
"monitored": monitored,
"online": snapshot.get("online"),
"offline": snapshot.get("offline"),
}