ForcePilot/backend/package/yuxi/channel/extensions/bluebubbles/pairing.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

67 lines
2.0 KiB
Python

import random
import time
import logging
logger = logging.getLogger(__name__)
PAIRING_CODE_LENGTH = 6
PAIRING_CODE_EXPIRY_SECONDS = 3600
MAX_PENDING_PER_CHANNEL = 3
PAIRING_APPROVED_MESSAGE = "BlueBubbles DM pairing approved. You can now send iMessages to this channel."
class PairingManager:
def __init__(self):
self._pending: dict[str, dict] = {}
self._approved: set[str] = set()
def generate_code(self, peer_id: str) -> str:
self._cleanup_expired()
active = sum(1 for v in self._pending.values() if v["status"] == "pending")
if active >= MAX_PENDING_PER_CHANNEL:
raise RuntimeError(f"Maximum pending pairings ({MAX_PENDING_PER_CHANNEL}) reached")
code = "".join(str(random.randint(0, 9)) for _ in range(PAIRING_CODE_LENGTH))
self._pending[peer_id] = {
"code": code,
"created_at": time.time(),
"status": "pending",
}
return code
def verify_code(self, peer_id: str, code: str) -> bool:
entry = self._pending.get(peer_id)
if not entry:
return False
if time.time() - entry["created_at"] > PAIRING_CODE_EXPIRY_SECONDS:
del self._pending[peer_id]
return False
if entry["code"] == code:
entry["status"] = "approved"
self._approved.add(peer_id)
return True
return False
def is_approved(self, peer_id: str) -> bool:
return peer_id in self._approved
def revoke(self, peer_id: str):
self._approved.discard(peer_id)
self._pending.pop(peer_id, None)
def _cleanup_expired(self):
now = time.time()
expired = [
pid for pid, entry in self._pending.items() if (now - entry["created_at"]) > PAIRING_CODE_EXPIRY_SECONDS
]
for pid in expired:
del self._pending[pid]
def pending_count(self) -> int:
self._cleanup_expired()
return sum(1 for v in self._pending.values() if v["status"] == "pending")