60 lines
2.3 KiB
Python
60 lines
2.3 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
import time
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from yuxi.utils.logging_config import logger
|
||
|
|
|
||
|
|
|
||
|
|
class SentMessageCache:
|
||
|
|
def __init__(self, storage_dir: str | None = None, max_entries: int = 1000):
|
||
|
|
self._storage_dir = Path(storage_dir or str(Path.home() / ".yuxi" / "imessage"))
|
||
|
|
self._max_entries = max_entries
|
||
|
|
self._entries: dict[str, dict[str, Any]] = {}
|
||
|
|
self._storage_dir.mkdir(parents=True, exist_ok=True)
|
||
|
|
self._load()
|
||
|
|
|
||
|
|
def record(self, message_id: str, content: str, chat_guid: str, metadata: dict[str, Any] | None = None) -> None:
|
||
|
|
entry = {
|
||
|
|
"message_id": message_id,
|
||
|
|
"content": content[:500],
|
||
|
|
"chat_guid": chat_guid,
|
||
|
|
"sent_at": time.time(),
|
||
|
|
"metadata": metadata or {},
|
||
|
|
}
|
||
|
|
self._entries[message_id] = entry
|
||
|
|
if len(self._entries) > self._max_entries:
|
||
|
|
oldest = min(self._entries.values(), key=lambda e: e["sent_at"])
|
||
|
|
self._entries.pop(oldest["message_id"], None)
|
||
|
|
|
||
|
|
def get(self, message_id: str) -> dict[str, Any] | None:
|
||
|
|
return self._entries.get(message_id)
|
||
|
|
|
||
|
|
def get_content(self, message_id: str) -> str | None:
|
||
|
|
entry = self._entries.get(message_id)
|
||
|
|
return entry["content"] if entry else None
|
||
|
|
|
||
|
|
def list_recent(self, limit: int = 50) -> list[dict[str, Any]]:
|
||
|
|
entries = sorted(self._entries.values(), key=lambda e: e["sent_at"], reverse=True)
|
||
|
|
return entries[:limit]
|
||
|
|
|
||
|
|
async def persist(self) -> None:
|
||
|
|
file_path = self._storage_dir / "sent_messages.json"
|
||
|
|
file_path.write_text(json.dumps(self._entries, ensure_ascii=False, indent=2), encoding="utf-8")
|
||
|
|
logger.debug(f"[iMessage/SentCache] Persisted {len(self._entries)} entries")
|
||
|
|
|
||
|
|
def _load(self) -> None:
|
||
|
|
file_path = self._storage_dir / "sent_messages.json"
|
||
|
|
if not file_path.exists():
|
||
|
|
return
|
||
|
|
try:
|
||
|
|
self._entries = json.loads(file_path.read_text(encoding="utf-8"))
|
||
|
|
now = time.time()
|
||
|
|
self._entries = {
|
||
|
|
mid: entry for mid, entry in self._entries.items() if now - entry.get("sent_at", 0) < 86400
|
||
|
|
}
|
||
|
|
except (json.JSONDecodeError, OSError):
|
||
|
|
self._entries = {}
|