Files
awoooi/apps/api/src/services/github_webhook_service.py
OG T 0060a33e31 feat(api): Phase 13.1 #74 GitHub Webhook → OpenClaw 整合
- POST /api/v1/webhooks/github endpoint
- 處理 pull_request 和 push 事件
- 驗證 X-Hub-Signature-256
- Telegram 通知整合
- GitHubWebhookService 封裝 Redis 操作 (leWOOOgo 合規)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 10:08:54 +08:00

122 lines
3.1 KiB
Python

"""
GitHub Webhook Service - Phase 13.1
====================================
封裝 GitHub Webhook 相關的 Redis 操作
遵循 leWOOOgo 積木化原則:
- Router 層不直接存取 Redis
- 透過 Service 層封裝資料存取邏輯
"""
import json
from typing import Protocol
import structlog
from src.core.redis_client import get_redis
logger = structlog.get_logger(__name__)
# Redis TTL: 7 天
GITHUB_REVIEW_TTL_SECONDS = 7 * 24 * 60 * 60
class IGitHubReviewRepository(Protocol):
"""GitHub Review Repository Interface"""
async def save_review(self, review_id: str, review_data: dict) -> bool:
"""儲存審查結果"""
...
async def get_review(self, review_id: str) -> dict | None:
"""取得審查結果"""
...
class GitHubReviewRedisRepository:
"""Redis 實作的 GitHub Review Repository"""
KEY_PREFIX = "github_review:"
async def save_review(self, review_id: str, review_data: dict) -> bool:
"""
儲存審查結果到 Redis
Args:
review_id: 審查 ID
review_data: 審查結果資料
Returns:
bool: 儲存是否成功
"""
try:
redis_client = get_redis()
key = f"{self.KEY_PREFIX}{review_id}"
await redis_client.set(
key,
json.dumps(review_data, ensure_ascii=False),
ex=GITHUB_REVIEW_TTL_SECONDS,
)
logger.debug("github_review_saved", review_id=review_id)
return True
except Exception as e:
logger.error("github_review_save_failed", review_id=review_id, error=str(e))
return False
async def get_review(self, review_id: str) -> dict | None:
"""
取得審查結果
Args:
review_id: 審查 ID
Returns:
審查結果資料,或 None
"""
try:
redis_client = get_redis()
key = f"{self.KEY_PREFIX}{review_id}"
result = await redis_client.get(key)
if result:
return json.loads(result)
return None
except Exception as e:
logger.error("github_review_get_failed", review_id=review_id, error=str(e))
return None
class GitHubWebhookService:
"""
GitHub Webhook 服務
封裝審查結果的儲存與查詢
"""
def __init__(self, repository: IGitHubReviewRepository | None = None):
self._repository = repository or GitHubReviewRedisRepository()
async def save_review_result(
self,
review_id: str,
review_data: dict,
) -> bool:
"""儲存審查結果"""
return await self._repository.save_review(review_id, review_data)
async def get_review_result(self, review_id: str) -> dict | None:
"""取得審查結果"""
return await self._repository.get_review(review_id)
# 單例
_service: GitHubWebhookService | None = None
def get_github_webhook_service() -> GitHubWebhookService:
"""取得 GitHubWebhookService 單例"""
global _service
if _service is None:
_service = GitHubWebhookService()
return _service