ForcePilot/backend/package/yuxi/channel/extensions/pinduoduo/config.py
Kris 1476e82ef0 feat(channel): 添加拼多多渠道扩展
新增拼多多(Pinduoduo)渠道扩展,支持在 Yuxi 平台中集成拼多多电商客服渠道。

包含以下功能模块:
- client: 拼多多 API 客户端封装
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- webhook: Webhook 事件处理
- outbound: 外发消息管理
- pairing: 用户配对与绑定
- security: 安全校验
- signature: 请求签名验证
- token: Token 管理
- dedupe: 消息去重
- monitor: 渠道状态监控
- status: 会话状态管理
- tools: Agent 工具集成
- window: 窗口管理
- types: 类型定义
2026-05-21 11:33:31 +08:00

152 lines
6.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import os
from yuxi.channel.extensions.pinduoduo.types import PinduoduoAccount
ENV_MAP = {
"client_id": "PDD_CLIENT_ID",
"client_secret": "PDD_CLIENT_SECRET",
"access_token": "PDD_ACCESS_TOKEN",
"refresh_token": "PDD_REFRESH_TOKEN",
"mall_id": "PDD_MALL_ID",
"dm_policy": "PDD_DM_POLICY",
"poll_interval_sec": "PDD_POLL_INTERVAL",
"enable_push": "PDD_ENABLE_PUSH",
"push_verify_token": "PDD_PUSH_VERIFY_TOKEN",
"sign_method": "PDD_SIGN_METHOD",
}
class PinduoduoConfig:
def list_account_ids(self, config: dict | None = None) -> list[str]:
accounts = self._get_accounts(config or {})
if accounts:
return list(accounts.keys())
return ["default"]
async def resolve_account(self, account_id: str, config: dict | None = None) -> PinduoduoAccount:
config = config or {}
accounts = self._get_accounts(config)
if account_id in accounts:
acct = accounts[account_id]
return PinduoduoAccount(
account_id=account_id,
client_id=acct.get("clientId", "") or self._env("client_id"),
client_secret=acct.get("clientSecret", "") or self._env("client_secret"),
access_token=acct.get("accessToken") or self._env("access_token") or None,
refresh_token=acct.get("refreshToken") or self._env("refresh_token") or None,
mall_id=acct.get("mallId", "") or self._env("mall_id"),
dm_policy=(acct.get("dmPolicy") or self._env("dm_policy") or "pairing"),
poll_interval_sec=int(acct.get("pollInterval") or self._env("poll_interval_sec") or 30),
enable_push=(acct.get("enablePush") or self._env("enable_push", "").lower() == "true" or False),
push_verify_token=(acct.get("pushVerifyToken") or self._env("push_verify_token") or None),
sign_method=(acct.get("signMethod") or self._env("sign_method") or "md5"),
)
return PinduoduoAccount(
account_id="default",
client_id=self._env("client_id"),
client_secret=self._env("client_secret"),
access_token=self._env("access_token") or None,
refresh_token=self._env("refresh_token") or None,
mall_id=self._env("mall_id"),
dm_policy=self._env("dm_policy") or "pairing",
poll_interval_sec=int(self._env("poll_interval_sec") or 30),
enable_push=(self._env("enable_push") or "").lower() == "true",
push_verify_token=self._env("push_verify_token") or None,
sign_method=self._env("sign_method") or "md5",
)
def is_configured(self, account: dict) -> bool:
return bool(account.get("client_id") and account.get("client_secret") and account.get("mall_id"))
def _is_account_configured(self, account: PinduoduoAccount) -> bool:
return bool(account.client_id and account.client_secret and account.mall_id)
@staticmethod
def _get_accounts(config: dict) -> dict:
return config.get("accounts", {})
@staticmethod
def _env(key: str, default: str = "") -> str:
env_key = ENV_MAP.get(key, "")
return os.getenv(env_key, default) if env_key else default
def config_schema(self) -> dict:
return {
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"title": "拼多多渠道配置",
"properties": {
"accounts": {
"type": "object",
"title": "账户配置",
"description": "按 account_id 组织的账户列表,支持多店铺",
"additionalProperties": {
"type": "object",
"properties": {
"clientId": {
"type": "string",
"title": "Client ID",
"description": "拼多多开放平台应用 Client ID",
},
"clientSecret": {
"type": "string",
"title": "Client Secret",
"x-ui-password": True,
},
"accessToken": {
"type": "string",
"title": "Access Token",
"description": "OAuth 授权后自动填充",
"x-ui-password": True,
},
"refreshToken": {
"type": "string",
"title": "Refresh Token",
"description": "OAuth 授权后自动填充",
"x-ui-password": True,
},
"mallId": {
"type": "string",
"title": "店铺 ID (Mall ID)",
},
"dmPolicy": {
"type": "string",
"title": "DM 策略",
"enum": ["pairing", "allowlist", "open", "disabled"],
"default": "pairing",
},
"pollInterval": {
"type": "integer",
"title": "轮询间隔(秒)",
"default": 30,
"minimum": 5,
"maximum": 300,
},
"enablePush": {
"type": "boolean",
"title": "启用消息推送",
"default": False,
},
"pushVerifyToken": {
"type": "string",
"title": "推送验证 Token",
"x-ui-password": True,
},
"signMethod": {
"type": "string",
"title": "签名算法",
"enum": ["md5", "hmac-sha256"],
"default": "md5",
"description": "MD5 为旧版HMAC-SHA256 为新版,根据应用创建时间选择",
},
},
"required": ["clientId", "clientSecret", "mallId"],
},
},
},
}