该提交实现了完整的ClickUp聊天渠道插件,包含以下核心功能: 1. 基础的@提及提取与格式化能力 2. 账号配对与会话管理 3. 消息流与流式回复支持 4. 重试限流与消息去重 5. 富文本格式转换与内容 sanitize 6. 安全策略与配置校验 7. Webhook接收与自动轮询回退 8. 消息收发、编辑、删除与回复 9. 频道与私信管理、反应功能 10. 完整的状态监控与健康检查
91 lines
3.4 KiB
Python
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
|