"""Incoming Webhook sender for Synology Chat. Provides an alternative send path via Synology Chat's Incoming Webhook URL, as a lighter-weight alternative to the DSM API direct connection. """ from __future__ import annotations from typing import Any import httpx from yuxi.channels.adapters.synologychat.send import assert_safe_media_url from yuxi.channels.models import DeliveryResult from yuxi.utils.logging_config import logger async def send_via_incoming_webhook( webhook_url: str, text: str, file_url: str | None = None, timeout: float = 30.0, client: httpx.AsyncClient | None = None, ) -> DeliveryResult: if not webhook_url: return DeliveryResult(success=False, error="Incoming webhook URL not configured") try: await assert_safe_media_url(webhook_url) except Exception as e: return DeliveryResult(success=False, error=f"SSRF check failed: {e}") payload: dict[str, Any] = {"text": text} if file_url: payload["file_url"] = file_url async def _do_send(http_client: httpx.AsyncClient) -> DeliveryResult: response = await http_client.post(webhook_url, data=payload) response.raise_for_status() result = response.json() if result.get("success"): return DeliveryResult(success=True, message_id=result.get("data", {}).get("message_id")) return DeliveryResult( success=False, error=f"Webhook send failed: {result.get('error', {})}", ) try: if client: return await _do_send(client) async with httpx.AsyncClient(timeout=httpx.Timeout(timeout)) as new_client: return await _do_send(new_client) except httpx.TimeoutException: return DeliveryResult(success=False, error="Incoming webhook request timed out") except httpx.HTTPStatusError as e: return DeliveryResult(success=False, error=f"Incoming webhook HTTP {e.response.status_code}") except Exception as e: logger.error(f"[SynologyChat] Incoming webhook send failed: {e}") return DeliveryResult(success=False, error=str(e)) def build_webhook_url(dsm_url: str, webhook_token: str) -> str: """Build a Synology Chat Incoming Webhook URL from DSM URL and token.""" base = dsm_url.rstrip("/") return f"{base}/webapi/entry.cgi?api=SYNO.Chat.External&method=incoming&version=1&token={webhook_token}"