ForcePilot/backend/package/yuxi/channel/extensions/synology_chat/accounts.py
Kris 156f716600 feat(channel): 添加 Synology Chat 渠道扩展
新增 Synology Chat 渠道扩展,支持在 Yuxi 平台中集成群晖 Synology Chat 即时通讯渠道。

包含以下功能模块:
- client: Synology Chat API 客户端封装
- accounts: 账户管理
- webhook: Webhook 事件处理
- security: 安全校验
- dedupe: 消息去重
- status: 会话状态管理
- session: 会话管理
- types: 类型定义
2026-05-21 11:44:45 +08:00

76 lines
3.2 KiB
Python

from __future__ import annotations
import os
from yuxi.channel.extensions.synology_chat.types import (
ACCOUNT_ID_DEFAULT,
BOT_NAME_DEFAULT,
RATE_LIMIT_DEFAULT,
WEBHOOK_PATH_DEFAULT,
WEBHOOK_PATH_SOURCE_DEFAULT,
WEBHOOK_PATH_SOURCE_EXPLICIT,
WEBHOOK_PATH_SOURCE_INHERITED,
DM_POLICY_ALLOWLIST,
ResolvedSynologyChatAccount,
)
def list_account_ids(config: dict) -> list[str]:
channel_cfg = config.get("channels", {}).get("synology-chat", {})
base_ids = [ACCOUNT_ID_DEFAULT]
named = list(channel_cfg.get("accounts", {}).keys())
return base_ids + named
def resolve_account(config: dict, account_id: str = ACCOUNT_ID_DEFAULT) -> ResolvedSynologyChatAccount:
channel_cfg = config.get("channels", {}).get("synology-chat", {})
account_overrides = channel_cfg.get("accounts", {}).get(account_id, {}) if account_id != ACCOUNT_ID_DEFAULT else {}
def _resolve(key: str, env_var: str | None = None, default: str | int | bool = "") -> str | int | bool:
if key in account_overrides:
return account_overrides[key]
if key in channel_cfg:
return channel_cfg[key]
if env_var and account_id == ACCOUNT_ID_DEFAULT:
env_val = os.environ.get(env_var, "")
if env_val:
return env_val
return default
def _resolve_list(key: str) -> list[str]:
if key in account_overrides:
val = account_overrides[key]
return val if isinstance(val, list) else []
if key in channel_cfg:
val = channel_cfg[key]
return val if isinstance(val, list) else []
if account_id == ACCOUNT_ID_DEFAULT:
env_val = os.environ.get("SYNOLOGY_ALLOWED_USER_IDS", "")
if env_val:
return [u.strip() for u in env_val.split(",") if u.strip()]
return []
def _resolve_webhook_path_source() -> str:
if account_overrides.get("webhookPath"):
return WEBHOOK_PATH_SOURCE_EXPLICIT
if account_id != ACCOUNT_ID_DEFAULT and channel_cfg.get("webhookPath"):
return WEBHOOK_PATH_SOURCE_INHERITED
return WEBHOOK_PATH_SOURCE_DEFAULT
return ResolvedSynologyChatAccount(
account_id=account_id,
enabled=bool(_resolve("enabled", default=True)),
token=str(_resolve("token", "SYNOLOGY_CHAT_TOKEN")),
incoming_url=str(_resolve("incomingUrl", "SYNOLOGY_CHAT_INCOMING_URL")),
nas_host=str(_resolve("nasHost", "SYNOLOGY_NAS_HOST", "localhost")),
webhook_path=str(_resolve("webhookPath", default=WEBHOOK_PATH_DEFAULT)),
webhook_path_source=_resolve_webhook_path_source(),
dm_policy=str(_resolve("dmPolicy", default=DM_POLICY_ALLOWLIST)),
allowed_user_ids=_resolve_list("allowedUserIds"),
rate_limit_per_minute=int(_resolve("rateLimitPerMinute", "SYNOLOGY_RATE_LIMIT", RATE_LIMIT_DEFAULT)),
bot_name=str(_resolve("botName", "OPENCLAW_BOT_NAME", BOT_NAME_DEFAULT)),
allow_insecure_ssl=bool(_resolve("allowInsecureSsl", default=False)),
dangerously_allow_name_matching=bool(_resolve("dangerouslyAllowNameMatching", default=False)),
dangerously_allow_inherited_webhook_path=bool(_resolve("dangerouslyAllowInheritedWebhookPath", default=False)),
)