상태 메시지(이모지)는 raw=True로 유지 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
49 lines
1.6 KiB
Python
49 lines
1.6 KiB
Python
"""Synology Chat — incoming webhook으로 응답 전송."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
import re
|
|
|
|
import httpx
|
|
|
|
from config import settings
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _strip_markdown(text: str) -> str:
|
|
"""마크다운 문법 제거 — Synology Chat은 렌더링 안 됨."""
|
|
text = re.sub(r'\*\*(.+?)\*\*', r'\1', text) # **bold**
|
|
text = re.sub(r'\*(.+?)\*', r'\1', text) # *italic*
|
|
text = re.sub(r'`(.+?)`', r'\1', text) # `code`
|
|
text = re.sub(r'^#{1,6}\s+', '', text, flags=re.MULTILINE) # ### headers
|
|
text = re.sub(r'^\s*[-*]\s+', '• ', text, flags=re.MULTILINE) # - list → •
|
|
return text
|
|
|
|
|
|
async def send_to_synology(text: str, *, raw: bool = False) -> bool:
|
|
"""Incoming webhook URL로 메시지 전송. raw=True면 마크다운 제거 안 함."""
|
|
if not settings.synology_incoming_url:
|
|
logger.warning("Synology incoming URL not configured")
|
|
return False
|
|
|
|
if not raw:
|
|
text = _strip_markdown(text)
|
|
payload = json.dumps({"text": text}, ensure_ascii=False)
|
|
|
|
try:
|
|
async with httpx.AsyncClient(verify=False, timeout=10.0) as client:
|
|
resp = await client.post(
|
|
settings.synology_incoming_url,
|
|
data={"payload": payload},
|
|
)
|
|
if resp.status_code == 200:
|
|
return True
|
|
logger.error("Synology send failed: %d %s", resp.status_code, resp.text)
|
|
return False
|
|
except Exception:
|
|
logger.exception("Failed to send to Synology Chat")
|
|
return False
|