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)