zioinfo-mail/_archive/messenger/routers/webhook.py
DESKTOP-TKLFCPR\ython 28d3ba4836 refactor(cleanup): commit folder reorganization - scripts/, _archive/, docs/ restructure
- Move backend/frontend/messenger/ old paths to _archive/
- Reorganize scripts into scripts/deploy, check, push, setup, misc
- Move docs (pptx, docx) to docs/
- Add .claude agents and skills for fullstack/folder-cleanup harness
- workspace/ projects remain intact

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 19:43:09 +09:00

106 lines
3.7 KiB
Python

import hashlib
import hmac
import json
from datetime import datetime
from uuid import uuid4
from fastapi import APIRouter, HTTPException, Request
# ws_relay의 broadcast 함수 임포트 (순환 참조 방지를 위해 지연 임포트)
router = APIRouter(prefix="/api")
WEBHOOK_SECRET = "guardia-webhook-secret-2026"
ITSM_URL = "http://localhost:8001" # ITSM 서버 주소
TYPE_KR = {"DEPLOY":"배포", "RESTART":"재기동", "LOG":"로그 분석",
"INQUIRY":"문의", "OTHER":"기타"}
def _verify(signature: str, body: bytes) -> bool:
expected = "sha256=" + hmac.new(
WEBHOOK_SECRET.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
def _new_msg_id() -> str:
return f"MSG-{datetime.now().strftime('%Y%m%d')}-{str(uuid4())[:8].upper()}"
def _new_sr() -> str:
return f"SR-{datetime.now().strftime('%Y%m%d')}-{str(uuid4())[:6].upper()}"
@router.post("/messenger/webhook")
async def receive_webhook(request: Request):
from routers.ws_relay import broadcast, room_channels
from core.bot import store_message
body = await request.body()
payload = json.loads(body)
# ── ITSM 완료 이벤트 처리 ─────────────────────────────────────────
if payload.get("event") == "itsm_complete":
room = payload.get("room", "ops")
sr_id = payload.get("sr_id", "")
title = payload.get("title", "SR 처리 완료")
s_type = TYPE_KR.get(payload.get("sr_type",""), payload.get("sr_type",""))
req_by = payload.get("requested_by", "고객")
server = payload.get("target_server", "")
summary = payload.get("result_summary", "처리 완료")
content = (
f"✅ **{title}** 처리 완료\n"
f"• SR: `{sr_id}` 유형: {s_type}\n"
f"• 요청자: {req_by} 대상: {server}\n"
f"• 결과: {summary}\n\n"
f"서비스에 만족하셨나요? 아래에서 평가해 주세요."
)
bot_msg = {
"message_id": _new_msg_id(),
"timestamp": datetime.now().isoformat(),
"room_id": room,
"sender": "GUARDiA-Bot",
"sender_type": "BOT",
"msg_type": "CHAT",
"content": content,
"is_widget": True,
"interactive_action": {
"type": "STAR_RATING",
"sr_id": sr_id,
"customer": req_by,
"itsm_url": ITSM_URL,
},
}
store_message(room, bot_msg)
await broadcast(room, bot_msg)
return {"status": "NOTIFIED", "room": room, "sr_id": sr_id}
# ── 일반 웹훅 (기존 로직) ─────────────────────────────────────────
if payload.get("bot") or not payload.get("text", "").strip():
return {"status": "IGNORED"}
sr_id = _new_sr()
return {"status": "ACCEPTED", "sr_id": sr_id}
@router.post("/sr/approve/{sr_id}")
async def approve_sr(sr_id: str):
return {"status": "APPROVED", "sr_id": sr_id, "message": f"SR {sr_id} 승인 완료 — 실행을 시작합니다."}
@router.post("/sr/reject/{sr_id}")
async def reject_sr(sr_id: str):
return {"status": "REJECTED", "sr_id": sr_id, "message": f"SR {sr_id} 반려 처리되었습니다."}
@router.post("/killswitch/confirm")
async def killswitch_confirm():
return {"status": "STOPPED", "message": "모든 진행 중인 작업이 중단되었습니다."}
@router.post("/killswitch/cancel")
async def killswitch_cancel():
return {"status": "CANCELLED", "message": "Kill-Switch 취소되었습니다."}