ForcePilot/backend/package/yuxi/channels/adapters/whatsapp/inbound_cache.py
Kris e9b57546ea feat(whatsapp): 新增WhatsApp适配器完整功能模块
新增大量WhatsApp适配器相关代码,包括账号管理、会话处理、消息收发、验证授权、媒体处理、互动命令、审批流程、健康检测等完整功能模块,搭建基础的Baileys协议WhatsApp接入能力
2026-05-12 00:51:58 +08:00

105 lines
3.3 KiB
Python

from __future__ import annotations
import time
from collections import OrderedDict
from typing import Any
from yuxi.utils.logging_config import logger
def _normalize_jid(jid: str) -> str:
return jid.split("@")[0].replace("+", "").replace(" ", "").replace("-", "")
def _is_group_jid(jid: str) -> bool:
return "@g.us" in jid
class InboundMessageCache:
def __init__(self, max_size: int = 500, ttl_seconds: int = 600):
self._cache: OrderedDict[str, dict[str, Any]] = OrderedDict()
self._max_size = max_size
self._ttl = ttl_seconds
def put(self, msg_id: str, jid: str, content: str, metadata: dict[str, Any] | None = None) -> None:
if not msg_id:
return
self._cache[msg_id] = {
"jid": jid,
"content": content[:500],
"timestamp": time.time(),
"metadata": metadata or {},
}
self._cache.move_to_end(msg_id)
self._evict()
def get(self, msg_id: str) -> dict[str, Any] | None:
entry = self._cache.get(msg_id)
if entry is None:
return None
if time.time() - entry["timestamp"] > self._ttl:
self._cache.pop(msg_id, None)
return None
return entry
def lookup_inbound_meta(self, target_jid: str, target_msg_id: str | None = None) -> dict[str, Any] | None:
if target_msg_id:
exact = self.get(target_msg_id)
if exact:
return exact
target_is_group = _is_group_jid(target_jid)
candidates: list[tuple[str, dict[str, Any], float]] = []
now = time.time()
for msg_id, entry in self._cache.items():
if now - entry["timestamp"] > self._ttl:
continue
entry_jid = entry.get("jid", "")
if target_is_group != _is_group_jid(entry_jid):
continue
candidates.append((msg_id, entry, entry["timestamp"]))
if len(candidates) >= 10:
break
if not candidates:
return None
candidates.sort(key=lambda x: x[2], reverse=True)
best_msg_id, best_entry, _ = candidates[0]
logger.debug(
f"InboundMessageCache: fuzzy match for {target_jid} -> msg_id={best_msg_id}, candidates={len(candidates)}"
)
return best_entry
def resolve_quoted_message_key(self, target_jid: str, quoted_msg_id: str | None = None) -> dict[str, Any] | None:
meta = None
if quoted_msg_id:
meta = self.get(quoted_msg_id)
if meta is None:
meta = self.lookup_inbound_meta(target_jid, None)
if meta is None:
return None
msg_jid = meta.get("jid", target_jid)
return {
"remoteJid": msg_jid,
"fromMe": False,
"id": quoted_msg_id or "",
}
def _evict(self) -> None:
now = time.time()
expired = [k for k, v in self._cache.items() if now - v["timestamp"] > self._ttl]
for k in expired:
del self._cache[k]
while len(self._cache) > self._max_size:
oldest = next(iter(self._cache))
del self._cache[oldest]
def clear(self) -> None:
self._cache.clear()
def __len__(self) -> int:
return len(self._cache)