ForcePilot/backend/package/yuxi/channel/extensions/clickup/webhook_mgmt.py
Kris a79fc8dd7b feat(clickup): 新增ClickUp聊天渠道插件完整实现
该提交实现了完整的ClickUp聊天渠道插件,包含以下核心功能:
1. 基础的@提及提取与格式化能力
2. 账号配对与会话管理
3. 消息流与流式回复支持
4. 重试限流与消息去重
5. 富文本格式转换与内容 sanitize
6. 安全策略与配置校验
7. Webhook接收与自动轮询回退
8. 消息收发、编辑、删除与回复
9. 频道与私信管理、反应功能
10. 完整的状态监控与健康检查
2026-05-21 10:43:19 +08:00

75 lines
2.6 KiB
Python

import logging
import httpx
logger = logging.getLogger(__name__)
API_V2_BASE = "https://api.clickup.com/api/v2"
async def create_webhook(
workspace_id: str,
api_token: str,
endpoint_url: str,
events: list[str],
*,
space_id: str | None = None,
folder_id: str | None = None,
list_id: str | None = None,
) -> dict:
url = f"{API_V2_BASE}/team/{workspace_id}/webhook"
headers = {"Authorization": api_token, "Content-Type": "application/json"}
payload: dict = {
"endpoint": endpoint_url,
"events": events,
}
if space_id:
payload["space_id"] = space_id
if folder_id:
payload["folder_id"] = folder_id
if list_id:
payload["list_id"] = list_id
async with httpx.AsyncClient(timeout=15.0) as client:
resp = await client.post(url, json=payload, headers=headers)
if resp.status_code in (200, 201):
return resp.json()
error_text = resp.text[:500]
raise RuntimeError(f"创建 webhook 失败 (HTTP {resp.status_code}): {error_text}")
async def list_webhooks(workspace_id: str, api_token: str) -> list[dict]:
url = f"{API_V2_BASE}/team/{workspace_id}/webhook"
headers = {"Authorization": api_token}
async with httpx.AsyncClient(timeout=15.0) as client:
resp = await client.get(url, headers=headers)
if resp.status_code == 200:
data = resp.json()
return data.get("webhooks", [])
error_text = resp.text[:500]
raise RuntimeError(f"获取 webhook 列表失败 (HTTP {resp.status_code}): {error_text}")
async def update_webhook(webhook_id: str, api_token: str, **updates) -> dict:
url = f"{API_V2_BASE}/webhook/{webhook_id}"
headers = {"Authorization": api_token, "Content-Type": "application/json"}
async with httpx.AsyncClient(timeout=15.0) as client:
resp = await client.put(url, json=updates, headers=headers)
if resp.status_code == 200:
return resp.json()
error_text = resp.text[:500]
raise RuntimeError(f"更新 webhook 失败 (HTTP {resp.status_code}): {error_text}")
async def delete_webhook(webhook_id: str, api_token: str) -> dict:
url = f"{API_V2_BASE}/webhook/{webhook_id}"
headers = {"Authorization": api_token}
async with httpx.AsyncClient(timeout=15.0) as client:
resp = await client.delete(url, headers=headers)
if resp.status_code in (200, 204):
return {"deleted": True, "webhook_id": webhook_id}
error_text = resp.text[:500]
raise RuntimeError(f"删除 webhook 失败 (HTTP {resp.status_code}): {error_text}")