ForcePilot/backend/package/yuxi/channels/adapters/nextcloudtalk/probe.py
Kris b018ad25da feat(backend): add Nextcloud Talk channel adapter implementation
实现了完整的Nextcloud Talk聊天适配器,包含以下核心功能:
1. 基础客户端通信与认证
2. 会话路由与聊天类型解析
3. 消息分块与格式转换
4. 用户配对与权限校验
5. 轮询式消息监听
6. 配置验证与诊断工具
7. 安全策略与去重缓存
8. 指标统计与状态快照

新增配套的配对用户缓存文件与模块导出入口。
2026-05-12 00:47:06 +08:00

67 lines
1.9 KiB
Python

from __future__ import annotations
import logging
import time
from typing import Any
from aiohttp import ClientSession
logger = logging.getLogger(__name__)
CAPABILITIES_ENDPOINT = "/ocs/v2.php/cloud/capabilities"
_CAPABILITIES_CACHE: dict[str, tuple[float, dict[str, Any]]] = {}
_CACHE_TTL = 300
async def probe_capabilities(http: ClientSession, server_url: str) -> dict[str, Any]:
now = time.time()
cached = _CAPABILITIES_CACHE.get(server_url)
if cached and now - cached[0] < _CACHE_TTL:
return cached[1]
url = f"{server_url}{CAPABILITIES_ENDPOINT}"
headers = {"OCS-APIRequest": "true", "Accept": "application/json"}
try:
async with http.get(url, headers=headers, timeout=10) as resp:
data = await resp.json()
ocs = data.get("ocs", {})
caps = ocs.get("data", {}).get("capabilities", {})
spreed = caps.get("spreed", {})
if not spreed:
logger.warning("[NextcloudTalk] spreed capability not found in response")
_CAPABILITIES_CACHE[server_url] = (now, spreed)
return spreed
except Exception as e:
logger.warning(f"[NextcloudTalk] capabilities probe failed: {e}")
return cached[1] if cached else {}
def invalidate_cache(server_url: str | None = None) -> None:
if server_url:
_CAPABILITIES_CACHE.pop(server_url, None)
else:
_CAPABILITIES_CACHE.clear()
def resolve_api_version(capabilities: dict[str, Any]) -> str:
version = capabilities.get("version", "")
if not version:
return "v1"
try:
major = int(version.split(".")[0])
except (ValueError, IndexError):
return "v1"
if major >= 17:
return "v4"
elif major >= 12:
return "v3"
return "v1"
def resolve_api_base(api_version: str) -> str:
return f"/ocs/v2.php/apps/spreed/api/{api_version}"