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

155 lines
6.8 KiB
Python

from __future__ import annotations
import os
from yuxi.channel.extensions.zalouser.sidecar_client import ZcaSidecarClient
DEFAULT_SIDECAR_URL = "http://localhost:3001"
class ZaloUserConfigAdapter:
def __init__(self):
self._config_cache: dict | None = None
def set_runtime_config(self, config: dict) -> None:
self._config_cache = config
def list_account_ids(self, config: dict) -> list[str]:
channels = config.get("channels", config)
zalouser_cfg = channels.get("zalouser", channels)
accounts = zalouser_cfg.get("accounts", {})
return list(accounts.keys()) if accounts else ["default"]
def resolve_sidecar_url(self, account: dict) -> str:
sidecar_url = account.get("sidecar_url", "")
if sidecar_url:
return sidecar_url
return os.getenv("ZALOUSER_SIDECAR_URL", DEFAULT_SIDECAR_URL)
def resolve_profile(self, account: dict) -> str:
profile = account.get("profile")
if profile:
return profile
return os.getenv("ZALOUSER_PROFILE", os.getenv("ZCA_PROFILE", "default"))
async def resolve_account(self, account_id: str) -> dict:
config = self._read_channel_config()
accounts = config.get("accounts", {})
account = accounts.get(account_id, {})
profile = self.resolve_profile(account)
sidecar_url = self.resolve_sidecar_url(account)
return {
"account_id": account_id,
"profile": profile,
"name": account.get("name", account_id),
"sidecar_url": sidecar_url,
"ping_interval": account.get("ping_interval", 30),
"ping_timeout": account.get("ping_timeout", 10),
"dm_policy": account.get("dm_policy", config.get("dm_policy", "pairing")),
"allow_from": account.get("allow_from", config.get("allow_from", [])),
"group_policy": account.get("group_policy", config.get("group_policy", "allowlist")),
"group_allow_from": account.get("group_allow_from", config.get("group_allow_from", [])),
"groups": account.get("groups", config.get("groups", {})),
"dangerously_allow_name_matching": account.get(
"dangerously_allow_name_matching",
config.get("dangerously_allow_name_matching", False),
),
"history_limit": account.get("history_limit", config.get("history_limit", 50)),
"text_chunk_limit": account.get("text_chunk_limit", config.get("text_chunk_limit", 2000)),
"message_prefix": account.get("message_prefix", config.get("message_prefix", "")),
"response_prefix": account.get("response_prefix", config.get("response_prefix", "")),
"config": config,
}
def is_configured(self, account: dict) -> bool:
return bool(account.get("sidecar_url", ""))
def is_enabled(self, account: dict, config: dict) -> bool:
return account.get("enabled", True) if isinstance(account.get("enabled"), bool) else True
def disabled_reason(self, account: dict, config: dict) -> str:
if not account.get("sidecar_url"):
return "Sidecar URL is required (set ZALOUSER_SIDECAR_URL env or configure sidecar_url)"
return ""
async def resolve_allow_from(self, config: dict, account_id: str) -> list[str] | None:
account = await self.resolve_account(account_id)
return account.get("allow_from", [])
def describe_account(self, account: dict) -> dict:
return {
"account_id": account.get("account_id", ""),
"name": account.get("name", ""),
"configured": self.is_configured(account),
"sidecar_url": account.get("sidecar_url", ""),
}
def config_schema(self) -> dict:
return {
"type": "object",
"properties": {
"enabled": {"type": "boolean", "default": True},
"default_account": {"type": "string", "default": "default"},
"accounts": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"profile": {"type": "string", "default": "default"},
"name": {"type": "string"},
"sidecar_url": {"type": "string"},
"dm_policy": {
"type": "string",
"enum": ["pairing", "allowlist", "open", "disabled"],
"default": "pairing",
},
"allow_from": {
"type": "array",
"items": {"type": "string"},
},
"group_policy": {
"type": "string",
"enum": ["open", "allowlist", "disabled"],
"default": "allowlist",
},
"group_allow_from": {
"type": "array",
"items": {"type": "string"},
},
"groups": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"enabled": {"type": "boolean", "default": True},
"require_mention": {"type": "boolean", "default": True},
},
},
},
"dangerously_allow_name_matching": {
"type": "boolean",
"default": False,
},
"history_limit": {"type": "integer", "default": 50},
"text_chunk_limit": {"type": "integer", "default": 2000},
"message_prefix": {"type": "string", "default": ""},
"response_prefix": {"type": "string", "default": ""},
},
},
},
},
}
def build_sidecar_client(self, account: dict) -> ZcaSidecarClient:
return ZcaSidecarClient(
account.get("sidecar_url", DEFAULT_SIDECAR_URL),
profile=account.get("profile", "default"),
)
def _read_channel_config(self) -> dict:
if self._config_cache:
channels = self._config_cache.get("channels", self._config_cache)
return channels.get("zalouser", self._config_cache)
return {}