여러 줄 주석이 설명보다 경위(예전·실측·지적)를 적고 있어 읽는 사람이 결론을 찾기 어려웠다. - ts·tsx·js·mjs·css·py 478개: 여러 줄 주석은 첫 문장 한 줄로, 과거형·날짜 문장은 삭제 - 주석 위치는 TypeScript 파서·파이썬 tokenize/ast 로 찾는다 — 문자열 안의 # · /* 는 건드리지 않는다 - eslint·ts·noqa·type: ignore 같은 지시 주석은 그대로 둔다 파이썬 275개 정리 전후 AST 동일, TS 298개 주석 뺀 토큰 동일(빈 JSX 주석 10곳만 차이). site·frontend·admin tsc, site vitest 105 passed Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
56 lines
1.7 KiB
Python
56 lines
1.7 KiB
Python
"""Microsoft Teams Workflows(수신 webhook) 로 어댑티브 카드 한 장을 보낸다."""
|
|
import os
|
|
|
|
import httpx
|
|
|
|
from common.logger import LOG
|
|
|
|
WEBHOOK_URL_ENV = "TEAMS_WEBHOOK_URL"
|
|
TIMEOUT_SEC = 10.0
|
|
|
|
|
|
def is_configured() -> bool:
|
|
return bool(os.environ.get(WEBHOOK_URL_ENV, "").strip())
|
|
|
|
|
|
def _webhook_url() -> str:
|
|
return os.environ.get(WEBHOOK_URL_ENV, "").strip()
|
|
|
|
|
|
def _card(title: str, detail: str) -> dict:
|
|
"""Adaptive Card 1.2 — Teams Workflows 가 받는 최소 모양(search_console_alerts.py 와 같은 스키마)."""
|
|
return {
|
|
"type": "message",
|
|
"attachments": [{
|
|
"contentType": "application/vnd.microsoft.card.adaptive",
|
|
"contentUrl": None,
|
|
"content": {
|
|
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
|
"type": "AdaptiveCard",
|
|
"version": "1.2",
|
|
"body": [
|
|
{"type": "TextBlock", "text": title, "weight": "Bolder", "wrap": True},
|
|
{"type": "TextBlock", "text": detail, "wrap": True},
|
|
],
|
|
},
|
|
}],
|
|
}
|
|
|
|
|
|
async def send(title: str, detail: str) -> bool:
|
|
"""설정된 경우에만 보낸다."""
|
|
url = _webhook_url()
|
|
if not url:
|
|
return False
|
|
if not url.startswith("https://"):
|
|
LOG.w("[alert] TEAMS_WEBHOOK_URL_INVALID — https 가 아니다")
|
|
return False
|
|
try:
|
|
async with httpx.AsyncClient(timeout=TIMEOUT_SEC, follow_redirects=False) as client:
|
|
response = await client.post(url, json=_card(title, detail))
|
|
response.raise_for_status()
|
|
return True
|
|
except httpx.HTTPError:
|
|
LOG.w("[alert] TEAMS_DELIVERY_FAILED")
|
|
return False
|