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

68 lines
1.9 KiB
Python

from __future__ import annotations
import random
import time
from dataclasses import dataclass, field
PAIRING_CODE_TTL = 600
PAIRING_RATE_LIMIT = 3600
CODE_LENGTH = 6
@dataclass
class PendingPairing:
buyer_id: str
code: str
created_at: float = field(default_factory=time.time)
def is_expired(self) -> bool:
return time.time() - self.created_at > PAIRING_CODE_TTL
class PinduoduoPairing:
def __init__(self):
self._pending: dict[str, PendingPairing] = {}
self._last_generated: dict[str, float] = {}
self._paired: set[str] = set()
def generate_code(self, buyer_id: str) -> str | None:
last = self._last_generated.get(buyer_id, 0)
if time.time() - last < PAIRING_RATE_LIMIT:
return None
self._clean_expired()
active = [p for p in self._pending.values() if p.buyer_id == buyer_id and not p.is_expired()]
if len(active) >= 3:
return None
code = f"{random.randint(0, 999999):06d}"
self._pending[buyer_id] = PendingPairing(buyer_id=buyer_id, code=code)
self._last_generated[buyer_id] = time.time()
return code
def verify_code(self, buyer_id: str, code: str) -> bool:
self._clean_expired()
pending = self._pending.get(buyer_id)
if pending is None:
return False
if pending.is_expired():
return False
if pending.code != code:
return False
self._paired.add(buyer_id)
del self._pending[buyer_id]
return True
def is_paired(self, buyer_id: str) -> bool:
return buyer_id in self._paired
@staticmethod
def normalize_allow_entry(entry: str) -> str:
return entry.strip().lower()
def _clean_expired(self) -> None:
expired = [bid for bid, p in self._pending.items() if p.is_expired()]
for bid in expired:
del self._pending[bid]