这是一个批量整理提交,包含以下主要改动: 1. 删除多处冗余的空行和未使用的导入 2. 修复文件末尾缺少换行符的问题 3. 调整部分模块的导入顺序与代码排版 4. 修复部分配置默认值与策略逻辑 5. 新增多个功能模块与辅助工具 6. 完善异常处理与日志记录 7. 修复速率限制、消息缓存、权限校验等逻辑bug 8. 废弃部分旧有API与配置项并添加警告提示
66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
"""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}"
|