ForcePilot/backend/package/yuxi/channel/extensions/bluebubbles/history.py
Kris b018e3eda4 feat(bluebubbles): 新增BlueBubbles(iMessage)渠道插件完整实现
该提交新增了完整的BlueBubbles渠道插件,支持通过BlueBubbles Server集成iMessage功能,包含以下核心能力:
1.  支持私聊和群聊会话管理,自动区分会话类型
2.  完整的消息收发支持,包括文本、图片、语音、文件、视频消息
3.  支持消息反应、已读回执、消息编辑与撤回
4.  内置去重、防抖处理机制
5.  支持Webhook和WebSocket两种事件接收方式
6.  完善的权限与安全校验机制
7.  历史消息同步与抓包功能
8.  TTS语音合成与发送支持
9.  群组管理能力,包括重命名、修改头像、增减成员等
2026-05-21 10:40:33 +08:00

63 lines
2.0 KiB
Python

import logging
logger = logging.getLogger(__name__)
MAX_HISTORY_FETCH_LIMIT = 100
MAX_HISTORY_BODY_CHARS = 2000
async def fetch_chat_history(
client,
chat_guid: str,
limit: int = 50,
before_guid: str | None = None,
) -> list[dict]:
params = {"limit": min(limit, MAX_HISTORY_FETCH_LIMIT)}
if before_guid:
params["before"] = before_guid
for path, extra_params in _history_api_paths(chat_guid):
merged = {**params, **extra_params}
try:
resp = await client.get(path, params=merged)
if resp.status_code < 300:
data = _extract_message_list(resp.json())
return [_normalize_history_message(m) for m in data]
except Exception as e:
logger.debug("History fetch failed for %s: %s", path, e)
continue
return []
def _history_api_paths(chat_guid: str) -> list[tuple[str, dict]]:
return [
(f"/api/v1/chat/{chat_guid}/messages", {"sort": "DESC"}),
("/api/v1/messages", {"chatGuid": chat_guid}),
(f"/api/v1/chat/{chat_guid}/message", {}),
]
def _extract_message_list(response_data) -> list[dict]:
if isinstance(response_data, list):
return response_data
if isinstance(response_data, dict):
for key in ("data", "messages", "results"):
val = response_data.get(key)
if isinstance(val, list):
return val
return []
def _normalize_history_message(msg: dict) -> dict:
body = msg.get("text", msg.get("message", msg.get("body", "")))
return {
"guid": msg.get("guid", ""),
"text": body[:MAX_HISTORY_BODY_CHARS],
"sender": msg.get("sender", msg.get("handle", {}).get("id", "")),
"date": msg.get("dateDelivered", msg.get("date_delivered", msg.get("date", 0))),
"is_from_me": msg.get("isFromMe", msg.get("is_from_me", False)),
"chat_guid": msg.get("chatGuid", msg.get("chat_guid", "")),
"attachments": msg.get("attachments", []),
}