from __future__ import annotations
from loguru import logger
from datetime import datetime
from typing import Any, Optional
from aiogram import Bot
try:
from .config import settings
from .text_formatter import split_message_chunks
except (ImportError, ValueError):
from config import settings
from text_formatter import split_message_chunks
class AdminNotifier:
def __init__(self, bot: Bot) -> None:
self.bot = bot
self.admin_ids = settings.admin_id_list
async def send_to_all(self, text: str) -> None:
if not self.admin_ids:
return
for chunk in split_message_chunks(text, 4000):
for admin_id in self.admin_ids:
try:
await self.bot.send_message(
chat_id=admin_id,
text=chunk,
parse_mode="HTML",
disable_web_page_preview=True,
)
except Exception as exc:
logger.warning("Failed to send report to admin {}: {}", admin_id, exc)
def _route_section(self, route_name: str, vk_url: str, found_posts: list[dict[str, Any]], error: Optional[str]) -> Optional[str]:
if error:
return (
f"⚠️ {route_name} (VK) — ошибка проверки:\n"
f"{error}"
)
if not found_posts:
if not settings.report_empty_runs:
return None
return f"ℹ️ {route_name} (VK) — новых постов не обнаружено."
lines = [f"🚀 {route_name} (VK) — опубликовано: {len(found_posts)}"]
for idx, p in enumerate(found_posts, 1):
post_id = p.get("vk_post_id")
vk_post_url = p.get("vk_post_url")
tg_url = p.get("tg_url")
max_url = p.get("max_url")
tg_status = p.get("tg_status")
max_status = p.get("max_status")
tg_err = p.get("tg_error")
max_err = p.get("max_error")
lines.append(f"\n{idx}. Пост #{post_id}")
lines.append(f"• Оригинал в VK")
if tg_status == "published":
tg_link = f"Ссылка" if tg_url else "Опубликовано"
lines.append(f"• Telegram: ✅ {tg_link}")
elif tg_err:
lines.append(f"• Telegram: ❌ Ошибка: {tg_err[:100]}")
if max_status == "published":
max_link = f"Ссылка" if max_url else "Опубликовано"
lines.append(f"• MAX: ✅ {max_link}")
elif max_err:
lines.append(f"• MAX: ❌ Ошибка: {max_err[:100]}")
return "\n".join(lines)
async def notify_cycle_result(self, route_results: list[dict[str, Any]]) -> None:
"""route_results: one entry per route this cycle -
{"route_name": str, "vk_url": str, "reports": list[dict], "error": Optional[str]}
Sent as a single aggregated report per cycle (chunked if too long) rather
than one message per route, to avoid flooding admins when there are several
donor->recipient routes.
"""
if not self.admin_ids:
return
sections: list[str] = []
for rr in route_results:
section = self._route_section(
rr.get("route_name", rr.get("route_id", "?")),
rr.get("vk_url", ""),
rr.get("reports", []),
rr.get("error"),
)
if section:
sections.append(section)
if not sections:
return
now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
text = f"[VK Poster] Итоги проверки — {now_str}\n\n" + "\n\n".join(sections)
await self.send_to_all(text)