该提交新增了完整的BlueBubbles渠道插件,支持通过BlueBubbles Server集成iMessage功能,包含以下核心能力: 1. 支持私聊和群聊会话管理,自动区分会话类型 2. 完整的消息收发支持,包括文本、图片、语音、文件、视频消息 3. 支持消息反应、已读回执、消息编辑与撤回 4. 内置去重、防抖处理机制 5. 支持Webhook和WebSocket两种事件接收方式 6. 完善的权限与安全校验机制 7. 历史消息同步与抓包功能 8. TTS语音合成与发送支持 9. 群组管理能力,包括重命名、修改头像、增减成员等
67 lines
2.0 KiB
Python
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")
|