新增 RingCentral 渠道扩展,支持在 Yuxi 平台中集成 RingCentral 统一通信平台。 包含以下功能模块: - sdk: RingCentral SDK 封装 - config: 渠道配置管理 - gateway: SSE/WebSocket 网关接入 - webhook: Webhook 事件处理 - subscription: 事件订阅 - outbound: 外发消息管理 - streaming: 流式消息处理 - pairing: 用户配对与绑定 - security: 安全校验 - dedupe: 消息去重 - monitor: 渠道状态监控 - status: 会话状态管理 - session: 会话管理 - events: 事件处理 - adaptive_cards: 自适应卡片 - formatting: 格式化 - media: 媒体资源处理 - mentions: @提及 - notes: 笔记功能 - reactions: 表情反应 - tasks: 任务管理 - teams: 团队管理 - types: 类型定义
168 lines
7.2 KiB
Python
168 lines
7.2 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
|
|
from yuxi.channel.extensions.ringcentral.types import ResolvedRingCentralAccount
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_DEFAULT_WEBHOOK_PATH = "/api/webhook/ringcentral"
|
|
|
|
|
|
class RingCentralConfigAdapter:
|
|
DEFAULT_ACCOUNT_ID = "default"
|
|
|
|
def list_account_ids(self, config: dict) -> list[str]:
|
|
raw = config.get("channels", {}).get("ringcentral", {})
|
|
accounts = raw.get("accounts", {})
|
|
if accounts:
|
|
return list(accounts.keys())
|
|
if raw.get("client_id"):
|
|
return [self.DEFAULT_ACCOUNT_ID]
|
|
return []
|
|
|
|
async def resolve_account(self, account_id: str) -> dict:
|
|
from yuxi.channel.runtime.manager import gateway
|
|
|
|
config = gateway.global_config if gateway is not None else {}
|
|
rc_config = config.get("channels", {}).get("ringcentral", {}) if config else {}
|
|
|
|
defaults = rc_config.get("accounts", {}).get("default", {})
|
|
account_cfg = rc_config.get("accounts", {}).get(account_id, {})
|
|
merged = {**rc_config, **defaults, **account_cfg}
|
|
|
|
resolved = self._build_resolved(account_id, merged)
|
|
|
|
return {
|
|
"account_id": account_id,
|
|
"name": resolved.name,
|
|
"client_id": resolved.client_id,
|
|
"client_secret": resolved.client_secret,
|
|
"server_url": resolved.server_url,
|
|
"jwt_token": resolved.jwt_token,
|
|
"webhook_path": resolved.webhook_path,
|
|
"webhook_validation_token": resolved.webhook_validation_token,
|
|
"bot_person_id": resolved.bot_person_id,
|
|
"subscription_id": resolved.subscription_id,
|
|
"dm_policy": resolved.dm_policy,
|
|
"dm_enabled": resolved.dm_enabled,
|
|
"dm_allow_from": resolved.dm_allow_from,
|
|
"group_policy": resolved.group_policy,
|
|
"require_mention": resolved.require_mention,
|
|
"text_chunk_limit": resolved.text_chunk_limit,
|
|
"media_max_mb": resolved.media_max_mb,
|
|
"enabled": resolved.enabled,
|
|
"config": resolved.config,
|
|
"resolved": resolved,
|
|
}
|
|
|
|
def _build_resolved(self, account_id: str, merged: dict) -> ResolvedRingCentralAccount:
|
|
client_id = merged.get("client_id", "") or os.getenv("RINGCENTRAL_CLIENT_ID", "")
|
|
client_secret = merged.get("client_secret", "") or os.getenv("RINGCENTRAL_CLIENT_SECRET", "")
|
|
jwt_token = merged.get("jwt_token", "") or os.getenv("RINGCENTRAL_JWT_TOKEN", "")
|
|
|
|
return ResolvedRingCentralAccount(
|
|
account_id=account_id,
|
|
name=merged.get("name", account_id),
|
|
client_id=client_id,
|
|
client_secret=client_secret,
|
|
server_url=merged.get("server_url", "https://platform.ringcentral.com"),
|
|
jwt_token=jwt_token,
|
|
webhook_path=merged.get("webhook_path", _DEFAULT_WEBHOOK_PATH),
|
|
webhook_validation_token=merged.get("webhook_validation_token", ""),
|
|
bot_person_id=merged.get("bot_person_id", ""),
|
|
subscription_id=merged.get("subscription_id", ""),
|
|
dm_policy=merged.get("dm_policy", "pairing"),
|
|
dm_enabled=merged.get("dm_enabled", True),
|
|
dm_allow_from=merged.get("dm_allow_from", []),
|
|
group_policy=merged.get("group_policy", "allowlist"),
|
|
require_mention=merged.get("require_mention", True),
|
|
text_chunk_limit=merged.get("text_chunk_limit", 1000),
|
|
media_max_mb=merged.get("media_max_mb", 20),
|
|
enabled=merged.get("enabled", True),
|
|
config=merged,
|
|
)
|
|
|
|
def is_configured(self, account: dict) -> bool:
|
|
resolved = account.get("resolved")
|
|
if isinstance(resolved, ResolvedRingCentralAccount):
|
|
return resolved.is_configured
|
|
return bool(account.get("client_id") and account.get("jwt_token"))
|
|
|
|
def is_enabled(self, account: dict) -> bool:
|
|
return bool(account.get("enabled", True))
|
|
|
|
def disabled_reason(self, account: dict) -> str:
|
|
if not self.is_configured(account):
|
|
return "RingCentral client_id, client_secret and jwt_token are required"
|
|
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("dm_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),
|
|
}
|
|
|
|
def config_schema(self) -> dict:
|
|
return {
|
|
"type": "object",
|
|
"properties": {
|
|
"enabled": {"type": "boolean", "default": True},
|
|
"name": {"type": "string"},
|
|
"clientId": {"type": "string"},
|
|
"clientSecret": {"type": "string"},
|
|
"serverUrl": {
|
|
"type": "string",
|
|
"default": "https://platform.ringcentral.com",
|
|
},
|
|
"jwtToken": {"type": "string"},
|
|
"webhookPath": {"type": "string", "default": _DEFAULT_WEBHOOK_PATH},
|
|
"webhookValidationToken": {"type": "string"},
|
|
"botPersonId": {"type": "string"},
|
|
"subscriptionId": {"type": "string"},
|
|
"dmPolicy": {
|
|
"type": "string",
|
|
"enum": ["open", "pairing", "allowlist", "disabled"],
|
|
"default": "pairing",
|
|
},
|
|
"dmEnabled": {"type": "boolean", "default": True},
|
|
"dmAllowFrom": {"type": "array", "items": {"type": "string"}},
|
|
"groupPolicy": {
|
|
"type": "string",
|
|
"enum": ["open", "allowlist", "disabled"],
|
|
"default": "allowlist",
|
|
},
|
|
"requireMention": {"type": "boolean", "default": True},
|
|
"textChunkLimit": {"type": "integer", "default": 1000},
|
|
"mediaMaxMb": {"type": "integer", "default": 20},
|
|
"accounts": {
|
|
"type": "object",
|
|
"additionalProperties": {
|
|
"type": "object",
|
|
"properties": {
|
|
"name": {"type": "string"},
|
|
"clientId": {"type": "string"},
|
|
"clientSecret": {"type": "string"},
|
|
"serverUrl": {"type": "string"},
|
|
"jwtToken": {"type": "string"},
|
|
"webhookPath": {"type": "string"},
|
|
"webhookValidationToken": {"type": "string"},
|
|
"botPersonId": {"type": "string"},
|
|
"subscriptionId": {"type": "string"},
|
|
"dmPolicy": {"type": "string"},
|
|
"dmEnabled": {"type": "boolean"},
|
|
"dmAllowFrom": {"type": "array", "items": {"type": "string"}},
|
|
"groupPolicy": {"type": "string"},
|
|
"requireMention": {"type": "boolean"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|