ForcePilot/backend/package/yuxi/channels/adapters/telegram/thread_persistence.py
Kris 71fec609bb feat(telegram-adapter): 实现完整的Telegram适配器基础代码
新增了Telegram适配器的全套基础模块,包括:
1.  核心适配器入口与会话工具
2.  账号管理、认证与配置系统
3.  连接相关的轮询、Webhook、更新偏移管理
4.  话题路由、管理与缓存系统
5.  消息反抖动、超时配置与工具类
6.  响应式UI与命令交互系统
7.  反应表情与通知系统
8.  审批与安全审计模块
9.  健康检查与状态监控
10. 贴纸缓存与视觉工具
11. 流式响应与协作功能
12. 群组迁移与目标归一化处理
2026-05-12 00:49:52 +08:00

82 lines
2.7 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 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()