from __future__ import annotations import asyncio import time from collections import OrderedDict class ChatNameCache: DEFAULT_TTL_S = 1800 DEFAULT_MAX_ENTRIES = 500 def __init__(self, ttl_s: int = DEFAULT_TTL_S, max_entries: int = DEFAULT_MAX_ENTRIES): self._ttl_s = ttl_s self._max_entries = max_entries self._cache: OrderedDict[str, tuple[str, float]] = OrderedDict() self._lock = asyncio.Lock() @staticmethod def _make_key(account_id: str, chat_id: str) -> str: return f"{account_id}:{chat_id}" async def get(self, account_id: str, chat_id: str) -> str | None: async with self._lock: key = self._make_key(account_id, chat_id) entry = self._cache.get(key) if entry is None: return None name, ts = entry if time.monotonic() - ts > self._ttl_s: self._cache.pop(key, None) return None self._cache.move_to_end(key) return name async def set(self, account_id: str, chat_id: str, name: str) -> None: async with self._lock: key = self._make_key(account_id, chat_id) self._cache[key] = (name, time.monotonic()) self._cache.move_to_end(key) if len(self._cache) > self._max_entries: self._cache.popitem(last=False) async def invalidate(self, account_id: str, chat_id: str) -> None: async with self._lock: key = self._make_key(account_id, chat_id) self._cache.pop(key, None) async def clear(self) -> None: async with self._lock: self._cache.clear()