"""Wysyłka powiadomień na Slacka — kanał, który człowiek realnie czyta. Powód powstania (2026-08-03): watchdog świeżości poprawnie wykrywał zamrożone źródła (vjav stał 12 dni, streamporn.vip 23), ale sygnał ginął. Szedł wyłącznie do Sentry ze stabilnym fingerprintem i poziomem `warning`, więc powstawało JEDNO issue przy pierwszym wystąpieniu i potem tylko rósł mu licznik — Sentry powiadamia o nowych i regresjach, nie o kolejnych wystąpieniach otwartego issue. Domyślnie WYŁĄCZONE: bez `GOON_SLACK_BOT_TOKEN` i `GOON_SLACK_CHANNEL` funkcja jest no-opem i nic nie próbuje wysyłać. Nie zgadujemy kanału. """ from __future__ import annotations import logging import httpx from app.config import get_settings log = logging.getLogger(__name__) _API = "https://slack.com/api/chat.postMessage" def send_slack(text: str, *, timeout: float = 15.0) -> bool: """Wyślij wiadomość na skonfigurowany kanał. Zwraca True gdy poszła. Cicho zwraca False gdy brak konfiguracji — to normalny stan, nie błąd. """ settings = get_settings() token = getattr(settings, "slack_bot_token", "") or "" channel = getattr(settings, "slack_channel", "") or "" if not token or not channel: return False try: r = httpx.post( _API, headers={"Authorization": f"Bearer {token}"}, json={"channel": channel, "text": text}, timeout=timeout, ) data = r.json() if not data.get("ok"): # Slack zwraca 200 nawet przy błędzie logicznym — sprawdzamy `ok`. log.warning("slack: wysyłka odrzucona: %s", data.get("error")) return False return True except Exception as e: log.warning("slack: wysyłka nieudana: %s", e) return False