fix: dedupe daily report Telegram in dual mode and show host in summary fallback
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -75,11 +75,14 @@ def _dispatch_lifecycle_channels(event: Event, *, db: Session | None, policy) ->
|
||||
|
||||
|
||||
def notify_daily_report(event: Event, *, db: Session | None = None) -> None:
|
||||
"""Оповещение по суточному отчёту (severity=info, вне порога policy)."""
|
||||
"""Оповещение по суточному отчёту (severity=info, вне порога policy).
|
||||
|
||||
При UseSAC=dual агент шлёт TG сам (telegram_via=agent) — SAC не дублирует.
|
||||
"""
|
||||
policy = get_effective_notification_policy(db)
|
||||
if not should_notify_event(event, db):
|
||||
return
|
||||
_dispatch_event_channels(event, db=db, policy=policy)
|
||||
_dispatch_lifecycle_channels(event, db=db, policy=policy)
|
||||
|
||||
|
||||
def notify_lifecycle(event: Event, *, db: Session | None = None) -> None:
|
||||
|
||||
@@ -405,7 +405,15 @@ def _format_event_body_html(event: Event) -> str:
|
||||
return re.sub(r"\n{3,}", "\n\n", text)
|
||||
body = _detail(details, "report_body", default=event.summary)
|
||||
if body != "-":
|
||||
return html.escape(body)
|
||||
header = (
|
||||
"📊 ЕЖЕДНЕВНЫЙ ОТЧЕТ МОНИТОРИНГА WINDOWS"
|
||||
if event.type == "report.daily.rdp"
|
||||
else "📊 ЕЖЕДНЕВНЫЙ ОТЧЕТ SSH МОНИТОРИНГА"
|
||||
)
|
||||
msg = f"<b>{html.escape(header)}</b>\n"
|
||||
msg += _line("🏢", "Сервер", host_label(event.host))
|
||||
msg += f"\n{html.escape(body)}"
|
||||
return msg
|
||||
return format_generic_event_html(event)
|
||||
if event.type in ("rdp.login.success", "rdp.login.failed"):
|
||||
return format_rdp_login_html(event)
|
||||
|
||||
@@ -226,3 +226,61 @@ def test_notify_lifecycle_skips_telegram_when_via_agent():
|
||||
notify_dispatch.notify_lifecycle(event)
|
||||
mock_tg.notify_event.assert_not_called()
|
||||
mock_wh.notify_event.assert_called_once()
|
||||
|
||||
|
||||
def test_notify_daily_report_skips_telegram_when_via_agent():
|
||||
event = Event(
|
||||
event_id="00000000-0000-4000-8000-000000000604",
|
||||
host_id=1,
|
||||
occurred_at=datetime(2026, 6, 3, 9, 0, tzinfo=timezone.utc),
|
||||
category="report",
|
||||
type="report.daily.rdp",
|
||||
severity="info",
|
||||
title="Ежедневный отчёт Windows",
|
||||
summary="RDP 24ч: успех 0, неудач 0, банов 0",
|
||||
details={"telegram_via": "agent", "report_body": "📊 body"},
|
||||
payload={},
|
||||
)
|
||||
policy = NotificationPolicyConfig(
|
||||
min_severity="warning",
|
||||
use_telegram=True,
|
||||
use_webhook=True,
|
||||
use_email=False,
|
||||
source="db",
|
||||
)
|
||||
with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy):
|
||||
with patch.object(notify_dispatch, "should_notify_event", return_value=True):
|
||||
with patch.object(notify_dispatch, "telegram_notify") as mock_tg:
|
||||
with patch.object(notify_dispatch, "webhook_notify") as mock_wh:
|
||||
notify_dispatch.notify_daily_report(event)
|
||||
mock_tg.notify_event.assert_not_called()
|
||||
mock_wh.notify_event.assert_called_once()
|
||||
|
||||
|
||||
def test_notify_daily_report_skips_telegram_when_via_agent():
|
||||
event = Event(
|
||||
event_id="00000000-0000-4000-8000-000000000604",
|
||||
host_id=1,
|
||||
occurred_at=datetime(2026, 6, 3, 9, 0, tzinfo=timezone.utc),
|
||||
category="report",
|
||||
type="report.daily.rdp",
|
||||
severity="info",
|
||||
title="Ежедневный отчёт Windows",
|
||||
summary="RDP 24ч: успех 0, неудач 0, банов 0",
|
||||
details={"telegram_via": "agent", "report_body": "📊 body"},
|
||||
payload={},
|
||||
)
|
||||
policy = NotificationPolicyConfig(
|
||||
min_severity="warning",
|
||||
use_telegram=True,
|
||||
use_webhook=True,
|
||||
use_email=False,
|
||||
source="db",
|
||||
)
|
||||
with patch.object(notify_dispatch, "get_effective_notification_policy", return_value=policy):
|
||||
with patch.object(notify_dispatch, "should_notify_event", return_value=True):
|
||||
with patch.object(notify_dispatch, "telegram_notify") as mock_tg:
|
||||
with patch.object(notify_dispatch, "webhook_notify") as mock_wh:
|
||||
notify_dispatch.notify_daily_report(event)
|
||||
mock_tg.notify_event.assert_not_called()
|
||||
mock_wh.notify_event.assert_called_once()
|
||||
|
||||
@@ -210,6 +210,27 @@ def test_lifecycle_footer_uses_telegram_via_agent_when_present():
|
||||
assert "📡 Оповещение: агент (rdp-login-monitor 1.2.30-SAC)" in text
|
||||
|
||||
|
||||
def test_daily_report_summary_fallback_includes_host():
|
||||
host = Host(hostname="srv1", display_name="K6A-DC5", ipv4="192.168.160.91", os_family="linux")
|
||||
event = Event(
|
||||
event_id="00000000-0000-4000-8000-000000000508",
|
||||
host_id=1,
|
||||
host=host,
|
||||
occurred_at=datetime(2026, 6, 3, 9, 0, tzinfo=timezone.utc),
|
||||
category="report",
|
||||
type="report.daily.ssh",
|
||||
severity="info",
|
||||
title="Ежедневный отчёт SSH",
|
||||
summary="SSH 24ч: успех 0, неудач 0, банов 0",
|
||||
details={"generated_by": "sac"},
|
||||
payload={},
|
||||
)
|
||||
text = format_event_telegram_html(event)
|
||||
assert "K6A-DC5" in text
|
||||
assert "SSH 24ч: успех 0" in text
|
||||
assert "ЕЖЕДНЕВНЫЙ ОТЧЕТ SSH" in text
|
||||
|
||||
|
||||
def test_rdp_shadow_control_template():
|
||||
host = Host(hostname="RDS01", display_name="RDS Farm", ipv4="10.0.0.5", os_family="windows")
|
||||
event = Event(
|
||||
|
||||
Reference in New Issue
Block a user