from __future__ import annotations import json import time from pathlib import Path from typing import Any from yuxi.utils.logging_config import logger class ThreadPersistence: def __init__(self, storage_path: str | None = None): self._storage_path = storage_path self._bindings: dict[str, dict[str, Any]] = {} self._load() def _load(self) -> None: if not self._storage_path: return try: path = Path(self._storage_path) if path.exists(): self._bindings = json.loads(path.read_text(encoding="utf-8")) logger.debug(f"[Telegram] Loaded {len(self._bindings)} thread bindings") except Exception as e: logger.warning(f"[Telegram] Failed to load thread bindings: {e}") self._bindings = {} def _save(self) -> None: if not self._storage_path: return try: path = Path(self._storage_path) path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(self._bindings, ensure_ascii=False, indent=2), encoding="utf-8") except Exception as e: logger.warning(f"[Telegram] Failed to save thread bindings: {e}") def bind_channel_to_thread( self, channel_id: str, thread_id: str, metadata: dict[str, Any] | None = None, ) -> None: entry = { "channel_id": channel_id, "thread_id": thread_id, "bound_at": time.time(), "metadata": metadata or {}, } self._bindings[f"channel:{channel_id}"] = entry self._bindings[f"thread:{thread_id}"] = entry self._save() logger.debug(f"[Telegram] Bound channel {channel_id} -> thread {thread_id}") def get_thread_for_channel(self, channel_id: str) -> str | None: binding = self._bindings.get(f"channel:{channel_id}") return binding["thread_id"] if binding else None def get_channel_for_thread(self, thread_id: str) -> str | None: binding = self._bindings.get(f"thread:{thread_id}") return binding["channel_id"] if binding else None def unbind_channel(self, channel_id: str) -> None: binding = self._bindings.pop(f"channel:{channel_id}", None) if binding: self._bindings.pop(f"thread:{binding['thread_id']}", None) self._save() def get_all_bindings(self) -> list[dict[str, Any]]: seen = set() result = [] for key, binding in self._bindings.items(): if key.startswith("channel:"): result.append(binding) seen.add(key) return result def clear(self) -> None: self._bindings.clear() self._save()