Spaces:
Sleeping
Sleeping
| """Persist platform messages belonging to FCC-managed conversations.""" | |
| from datetime import UTC, datetime | |
| from typing import Any | |
| class ManagedMessageLog: | |
| """Track managed inbound and outbound messages in insertion order.""" | |
| def __init__(self, *, cap: int | None = None) -> None: | |
| self._items: dict[str, list[dict[str, Any]]] = {} | |
| self._ids: dict[str, set[str]] = {} | |
| self._cap = cap | |
| def cap(self) -> int | None: | |
| return self._cap | |
| def from_json(cls, raw_log: Any, *, cap: int | None = None) -> "ManagedMessageLog": | |
| """Load current and legacy message-log entries.""" | |
| log = cls(cap=cap) | |
| if not isinstance(raw_log, dict): | |
| return log | |
| for chat_key, items in raw_log.items(): | |
| if not isinstance(chat_key, str) or not isinstance(items, list): | |
| continue | |
| for item in items: | |
| if not isinstance(item, dict): | |
| continue | |
| message_id = item.get("message_id") | |
| direction = str(item.get("direction") or "") | |
| kind = str(item.get("kind") or "") | |
| if message_id is None or direction not in {"in", "out"} or not kind: | |
| continue | |
| log._append( | |
| chat_key, | |
| str(message_id), | |
| ts=str(item.get("ts") or ""), | |
| direction=direction, | |
| kind=kind, | |
| ) | |
| return log | |
| def to_json(self) -> dict[str, list[dict[str, Any]]]: | |
| return {chat_key: list(items) for chat_key, items in self._items.items()} | |
| def record( | |
| self, | |
| *, | |
| platform: str, | |
| chat_id: str, | |
| message_id: str, | |
| direction: str, | |
| kind: str, | |
| ) -> bool: | |
| """Record one managed platform message.""" | |
| if direction not in {"in", "out"}: | |
| raise ValueError("Managed message direction must be 'in' or 'out'") | |
| if not kind: | |
| raise ValueError("Managed message kind cannot be empty") | |
| return self._append( | |
| make_chat_key(platform, chat_id), | |
| str(message_id), | |
| ts=datetime.now(UTC).isoformat(), | |
| direction=direction, | |
| kind=str(kind), | |
| ) | |
| def ids_for_chat(self, platform: str, chat_id: str) -> list[str]: | |
| chat_key = make_chat_key(platform, chat_id) | |
| return [str(item["message_id"]) for item in self._items.get(chat_key, [])] | |
| def remove_ids(self, platform: str, chat_id: str, message_ids: set[str]) -> bool: | |
| chat_key = make_chat_key(platform, chat_id) | |
| if not message_ids or chat_key not in self._items: | |
| return False | |
| before_count = len(self._items[chat_key]) | |
| retained = [ | |
| item | |
| for item in self._items[chat_key] | |
| if str(item["message_id"]) not in message_ids | |
| ] | |
| if retained: | |
| self._items[chat_key] = retained | |
| self._ids[chat_key] = {str(item["message_id"]) for item in retained} | |
| else: | |
| self._items.pop(chat_key) | |
| self._ids.pop(chat_key, None) | |
| return len(retained) != before_count | |
| def clear_chat(self, platform: str, chat_id: str) -> bool: | |
| chat_key = make_chat_key(platform, chat_id) | |
| removed = self._items.pop(chat_key, None) is not None | |
| self._ids.pop(chat_key, None) | |
| return removed | |
| def _append( | |
| self, | |
| chat_key: str, | |
| message_id: str, | |
| *, | |
| ts: str, | |
| direction: str, | |
| kind: str, | |
| ) -> bool: | |
| seen = self._ids.setdefault(chat_key, set()) | |
| if message_id in seen: | |
| return False | |
| self._items.setdefault(chat_key, []).append( | |
| { | |
| "message_id": message_id, | |
| "ts": ts, | |
| "direction": direction, | |
| "kind": kind, | |
| } | |
| ) | |
| seen.add(message_id) | |
| self._trim(chat_key) | |
| return True | |
| def _trim(self, chat_key: str) -> None: | |
| if self._cap is None or self._cap <= 0: | |
| return | |
| items = self._items.get(chat_key, []) | |
| if len(items) <= self._cap: | |
| return | |
| retained = items[-self._cap :] | |
| self._items[chat_key] = retained | |
| self._ids[chat_key] = {str(item["message_id"]) for item in retained} | |
| def make_chat_key(platform: str, chat_id: str) -> str: | |
| return f"{platform}:{chat_id}" | |