ForcePilot/backend/package/yuxi/channel/extensions/clickup/status.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

91 lines
3.4 KiB
Python

import logging
import httpx
from yuxi.channel.protocols import ChannelAccountSnapshot
logger = logging.getLogger(__name__)
class ClickUpStatus:
default_runtime = None
async def probe(self, account: dict | None = None) -> bool:
if not account:
return False
api_token = account.get("api_token", "")
if not api_token:
return False
headers = {"Authorization": api_token}
async with httpx.AsyncClient(timeout=15.0) as client:
try:
resp = await client.get(
"https://api.clickup.com/api/v2/team",
headers=headers,
)
if resp.status_code == 200:
teams = resp.json().get("teams", [])
logger.info("ClickUp probe OK: %d workspace(s) accessible", len(teams))
return True
else:
logger.warning("ClickUp probe failed: status=%d", resp.status_code)
return False
except Exception as e:
logger.warning("ClickUp probe exception: %s", e)
return False
def build_summary(self, snapshot: object) -> dict:
return {
"account_id": getattr(snapshot, "account_id", ""),
"status": "ok" if getattr(snapshot, "probe_ok", False) else "error",
}
def build_account_snapshot(
self,
account: dict,
config: dict,
runtime: ChannelAccountSnapshot | None = None,
probe_result: object | None = None,
audit: object | None = None,
) -> ChannelAccountSnapshot:
return ChannelAccountSnapshot(
account_id=account.get("account_id", "default"),
name=account.get("name", ""),
enabled=account.get("enabled", True),
configured=bool(account.get("api_token") and account.get("workspace_id")),
status_state="linked" if account.get("api_token") else "not-linked",
running=getattr(runtime, "running", False) if runtime else False,
connected=getattr(runtime, "connected", False) if runtime else False,
last_message_at=getattr(runtime, "last_message_at", None) if runtime else None,
health_state="ok" if probe_result else "unknown",
dm_policy=account.get("dm_policy", "open"),
)
def collect_status_issues(self, accounts: list[ChannelAccountSnapshot]) -> list:
issues = []
for acct in accounts:
if not getattr(acct, "configured", True):
issues.append(
{
"channel": "clickup",
"account_id": acct.account_id,
"kind": "not-configured",
"message": "ClickUp API Token 或 Workspace ID 未配置",
"fix": "设置 CLICKUP_API_TOKEN 和 CLICKUP_WORKSPACE_ID 环境变量",
}
)
elif not getattr(acct, "connected", True):
issues.append(
{
"channel": "clickup",
"account_id": acct.account_id,
"kind": "not-connected",
"message": "ClickUp gateway 未运行",
"fix": "检查 webhook 端点和 gateway 状态",
}
)
return issues