ForcePilot/backend/package/yuxi/channels/adapters/zalo_oa/dedup.py
Kris 30dd9e16f4 feat(zalo-oa): 实现完整的 Zalo OA 渠道适配器模块
新增 Zalo OA 官方账号完整集成能力,包含:
1. 基础通信能力:消息编解码、目标归一化、文本分块
2. 安全与校验:Webhook 签名验证、DM 策略管理、配对流程
3. 辅助工具:重复事件去重、请求限流、异常告警
4. 管理功能:账号多实例管理、配置验证、健康诊断
5. 扩展能力:媒体托管、视觉识别、TTS 语音合成
6. 运维支持:审计日志、状态监控、目录同步
2026-05-12 00:52:47 +08:00

65 lines
2.0 KiB
Python

from __future__ import annotations
import time
from enum import Enum, auto
from typing import Any
DEDUP_WINDOW_MS_DEFAULT = 300000
class EventState(Enum):
CLAIMED = auto()
COMMITTED = auto()
class EventDeduplicator:
def __init__(self, window_ms: int = DEDUP_WINDOW_MS_DEFAULT):
self._window_ms = window_ms
self._events: dict[str, tuple[float, EventState]] = {}
def _make_key(self, raw_payload: dict[str, Any]) -> str:
sender = raw_payload.get("sender", {})
message = raw_payload.get("message", {})
event_name = raw_payload.get("event_name", "")
sender_id = sender.get("id", "unknown")
msg_id = message.get("msg_id", "")
return f"{event_name}:{sender_id}:{msg_id}"
def claim(self, raw_payload: dict[str, Any]) -> bool:
key = self._make_key(raw_payload)
now = time.monotonic()
window_s = self._window_ms / 1000
if key in self._events:
claimed_at, state = self._events[key]
if now - claimed_at < window_s:
return False
del self._events[key]
self._events[key] = (now, EventState.CLAIMED)
return True
def commit(self, raw_payload: dict[str, Any]):
key = self._make_key(raw_payload)
if key in self._events:
created_at, _ = self._events[key]
self._events[key] = (created_at, EventState.COMMITTED)
def release(self, raw_payload: dict[str, Any]):
key = self._make_key(raw_payload)
self._events.pop(key, None)
self._cleanup()
def is_committed(self, raw_payload: dict[str, Any]) -> bool:
key = self._make_key(raw_payload)
if key in self._events:
_, state = self._events[key]
return state == EventState.COMMITTED
return False
def _cleanup(self):
boundary = time.monotonic() - self._window_ms / 1000
expired = [k for k, (v, _) in self._events.items() if v < boundary]
for k in expired:
del self._events[k]