74 lines
2.5 KiB
Python
74 lines
2.5 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import hashlib
|
||
|
|
import time
|
||
|
|
from collections import OrderedDict
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from yuxi.utils.logging_config import logger
|
||
|
|
|
||
|
|
|
||
|
|
class MessageDebouncer:
|
||
|
|
def __init__(self, max_entries: int = 1000, ttl_seconds: float = 300):
|
||
|
|
self._cache: OrderedDict[str, float] = OrderedDict()
|
||
|
|
self._max_entries = max_entries
|
||
|
|
self._ttl_seconds = ttl_seconds
|
||
|
|
|
||
|
|
def _cleanup(self, now: float) -> None:
|
||
|
|
expired = [k for k, ts in self._cache.items() if now - ts > self._ttl_seconds]
|
||
|
|
for k in expired:
|
||
|
|
del self._cache[k]
|
||
|
|
|
||
|
|
def build_debounce_key(self, update: dict[str, Any]) -> str | None:
|
||
|
|
message = update.get("message") or update.get("edited_message") or update.get("channel_post")
|
||
|
|
if not message:
|
||
|
|
return None
|
||
|
|
|
||
|
|
chat_id = message.get("chat", {}).get("id", "")
|
||
|
|
message_id = message.get("message_id", "")
|
||
|
|
text = message.get("text", "") or message.get("caption", "")
|
||
|
|
|
||
|
|
components = f"{chat_id}|{message_id}|{text[:100]}"
|
||
|
|
return hashlib.sha256(components.encode()).hexdigest()[:16]
|
||
|
|
|
||
|
|
def is_duplicate(self, debounce_key: str) -> bool:
|
||
|
|
now = time.monotonic()
|
||
|
|
self._cleanup(now)
|
||
|
|
|
||
|
|
if debounce_key in self._cache:
|
||
|
|
logger.debug(f"[Telegram] Debounced duplicate message: {debounce_key}")
|
||
|
|
return True
|
||
|
|
|
||
|
|
if len(self._cache) >= self._max_entries:
|
||
|
|
self._cache.popitem(last=False)
|
||
|
|
|
||
|
|
self._cache[debounce_key] = now
|
||
|
|
return False
|
||
|
|
|
||
|
|
def clear_entry(self, debounce_key: str) -> None:
|
||
|
|
self._cache.pop(debounce_key, None)
|
||
|
|
|
||
|
|
def cache_size(self) -> int:
|
||
|
|
return len(self._cache)
|
||
|
|
|
||
|
|
|
||
|
|
class ConnectTimeoutConfig:
|
||
|
|
def __init__(self, config: dict[str, Any] | None = None):
|
||
|
|
cfg = config or {}
|
||
|
|
self.connect_timeout = float(cfg.get("connect_timeout", 30))
|
||
|
|
self.read_timeout = float(cfg.get("read_timeout", 60))
|
||
|
|
self.write_timeout = float(cfg.get("write_timeout", 30))
|
||
|
|
self.pool_timeout = float(cfg.get("pool_timeout", 30))
|
||
|
|
|
||
|
|
def get_request_kwargs(self) -> dict[str, Any]:
|
||
|
|
timeout_value = self.read_timeout
|
||
|
|
if hasattr(self, "connect_timeout"):
|
||
|
|
timeout_value = float(self.connect_timeout)
|
||
|
|
|
||
|
|
return {
|
||
|
|
"connect_timeout": self.connect_timeout,
|
||
|
|
"read_timeout": self.read_timeout,
|
||
|
|
"write_timeout": self.write_timeout,
|
||
|
|
"pool_timeout": self.pool_timeout,
|
||
|
|
}
|