import logging import time from collections import OrderedDict logger = logging.getLogger("yuxi.channel.generic_webhook.dedupe") DEFAULT_TTL = 600 DEFAULT_MAX_SIZE = 10000 class WebhookDeduplicator: def __init__(self, max_size: int = DEFAULT_MAX_SIZE, ttl_seconds: int = DEFAULT_TTL): self._cache: OrderedDict[str, float] = OrderedDict() self._max_size = max_size self._ttl = ttl_seconds def is_duplicate(self, endpoint_id: str, event_id: str) -> bool: if not event_id: return False key = f"{endpoint_id}:{event_id}" now = time.monotonic() self._evict_expired(now) if key in self._cache: return True self._cache[key] = now while len(self._cache) > self._max_size: self._cache.popitem(last=False) return False def _evict_expired(self, now: float): threshold = now - self._ttl while self._cache: _, t = next(iter(self._cache.items())) if t >= threshold: break self._cache.popitem(last=False) def reset(self, endpoint_id: str | None = None): if endpoint_id: prefix = f"{endpoint_id}:" keys_to_remove = [k for k in self._cache if k.startswith(prefix)] for k in keys_to_remove: del self._cache[k] else: self._cache.clear() @property def size(self) -> int: return len(self._cache)