ForcePilot/backend/package/yuxi/channel/extensions/zalouser/status.py
Kris d5e36d33b7 feat(channel): 添加 Zalo OA、Zoom Chat 和 Zulip 渠道扩展
新增 Zalo OA、Zoom Chat、Zulip 三个渠道扩展。

Zalo OA 渠道扩展主要模块:sidecar_client, config, gateway, outbound, streaming, pairing, security, auth, dedupe, directory, monitor, status, session, reactions, tools

Zoom Chat 渠道扩展主要模块:config, gateway, webhook, outbound, streaming, pairing, security, crypto, dedupe, actions, media, mentions, monitor, status, session, reactions, threading

Zulip 渠道扩展主要模块:client, config, gateway, outbound, streaming, pairing, security, monitor, status
2026-05-21 12:06:26 +08:00

97 lines
3.7 KiB
Python

from __future__ import annotations
import logging
from yuxi.channel.extensions.zalouser.sidecar_client import ZcaSidecarClient
from yuxi.channel.protocols import ChannelAccountSnapshot
logger = logging.getLogger(__name__)
class ZaloUserStatus:
default_runtime = ChannelAccountSnapshot(account_id="default")
async def probe(self, account: dict | None = None, timeout_ms: int = 5000) -> dict:
if account is None:
return {"ok": False, "error": "No account provided", "elapsed_ms": 0}
sidecar_url = account.get("sidecar_url", "")
if not sidecar_url:
return {"ok": False, "error": "Sidecar URL is required", "elapsed_ms": 0}
import time
start = time.monotonic()
client = ZcaSidecarClient(sidecar_url, account.get("profile", "default"))
try:
status = await client.get_status()
elapsed_ms = int((time.monotonic() - start) * 1000)
authenticated = status.get("authenticated", False) or status.get("connected", False)
return {
"ok": True,
"authenticated": authenticated,
"sidecar_url": sidecar_url,
"elapsed_ms": elapsed_ms,
}
except Exception as e:
elapsed_ms = int((time.monotonic() - start) * 1000)
return {"ok": False, "error": str(e), "elapsed_ms": elapsed_ms}
finally:
await client.close()
def build_summary(self, snapshot: object) -> dict:
return {
"channel": "zalouser",
"accounts": 1,
}
def build_account_snapshot(
self,
account: dict,
config: dict,
runtime: ChannelAccountSnapshot | None = None,
probe_result: object | None = None,
audit: object | None = None,
) -> ChannelAccountSnapshot:
sidecar_url = account.get("sidecar_url", "")
configured = bool(sidecar_url)
status_state = "linked" if configured else "not-linked"
if probe_result is not None:
if isinstance(probe_result, dict):
if probe_result.get("authenticated"):
status_state = "authenticated"
elif probe_result.get("ok"):
status_state = "linked"
return ChannelAccountSnapshot(
account_id=account.get("account_id", ""),
name=account.get("name", ""),
enabled=account.get("enabled", True) if isinstance(account.get("enabled"), bool) else True,
configured=configured,
status_state=status_state,
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 and (isinstance(probe_result, dict) and probe_result.get("ok")) else "unknown"
),
dm_policy=account.get("dm_policy", "pairing"),
)
def collect_status_issues(self, accounts: list[ChannelAccountSnapshot]) -> list:
issues = []
for acc in accounts:
if not acc.configured:
issues.append(
{
"channel": "zalouser",
"account_id": acc.account_id,
"kind": "not-configured",
"message": "ZaloUser sidecar_url not configured",
"fix": (
"Set ZALOUSER_SIDECAR_URL environment variable or configure sidecar_url in channel settings"
),
}
)
return issues