웹훅 요청의 contentUrl 및 스키마 선언 누락을 공식 형식에 맞춤. HTTP 202와 채널 게시 성공을 구분하도록 검증 한계 기록. 검증: Teams 요청 형식 회귀 테스트 1건 통과. 워크플로 실제 수신은 별도 확인 필요.
46 lines
1.8 KiB
Python
46 lines
1.8 KiB
Python
"""Teams Workflow 수신용. 원문 오류/인증 정보는 메시지에 싣지 않는다."""
|
|
from datetime import timedelta
|
|
|
|
import httpx
|
|
|
|
from common.logger import LOG
|
|
|
|
|
|
def alert_reason(row, now, days: int) -> str | None:
|
|
if row.alerted_at and now - row.alerted_at < timedelta(days=1):
|
|
return None
|
|
if row.error_code:
|
|
return f"조회/제출 실패: {row.error_code}"
|
|
if (row.inspection or {}).get("verdict") == "PASS":
|
|
return None
|
|
if now - row.published_at >= timedelta(days=days):
|
|
return f"발행 후 {days}일 이상 색인 미확인"
|
|
return None
|
|
|
|
|
|
async def send_alert(webhook_url: str, page_url: str, reason: str) -> bool:
|
|
if not webhook_url:
|
|
return False
|
|
if not webhook_url.startswith("https://"):
|
|
LOG.w("[search-console] ALERT_URL_INVALID")
|
|
return False
|
|
payload = {"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": "Google 색인 확인 필요", "weight": "Bolder"},
|
|
{"type": "TextBlock", "text": page_url, "wrap": True},
|
|
{"type": "TextBlock", "text": reason, "wrap": True},
|
|
]},
|
|
}]}
|
|
try:
|
|
async with httpx.AsyncClient(timeout=10, follow_redirects=False) as client:
|
|
response = await client.post(webhook_url, json=payload)
|
|
response.raise_for_status()
|
|
return True
|
|
except httpx.HTTPError:
|
|
# webhook 주소 자체가 인증 수단이므로 예외 문자열도 로그에 남기지 않는다.
|
|
LOG.w("[search-console] ALERT_DELIVERY_FAILED")
|
|
return False
|