该提交新增了基于BlueBubbles的iMessage渠道插件,支持单聊和群组消息,包含文本、图片、语音、文件和视频消息收发,支持消息编辑、撤回、回复、 reactions和输入状态提示,同时实现了账号配置、安全校验、配对授权、消息格式化与分片等完整功能。
69 lines
2.0 KiB
Python
69 lines
2.0 KiB
Python
from __future__ import annotations
|
|
|
|
import secrets
|
|
import time
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
PAIRING_CODE_LENGTH = 8
|
|
PAIRING_CODE_EXPIRY_SECONDS = 600
|
|
PAIRING_CODE_CHARS = "ABCDEFGHJKMNPQRSTUVWXYZ23456789" # 无歧义: 排除 0/O/1/I
|
|
|
|
PAIRING_APPROVED_MESSAGE = (
|
|
"Your access has been approved. "
|
|
"You can now interact with this iMessage bot."
|
|
)
|
|
|
|
|
|
class IMessagePairingAdapter:
|
|
def __init__(self):
|
|
self._pending: dict[str, dict] = {}
|
|
self._approved: set[str] = set()
|
|
|
|
def generate_code(self, peer_id: str) -> str:
|
|
self._cleanup_expired()
|
|
code = "".join(secrets.choice(PAIRING_CODE_CHARS) for _ in range(PAIRING_CODE_LENGTH))
|
|
self._pending[peer_id] = {
|
|
"code": code,
|
|
"created_at": time.monotonic(),
|
|
"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.monotonic() - entry["created_at"] > PAIRING_CODE_EXPIRY_SECONDS:
|
|
del self._pending[peer_id]
|
|
return False
|
|
|
|
if secrets.compare_digest(entry["code"], code.upper().strip()):
|
|
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) -> None:
|
|
self._approved.discard(peer_id)
|
|
self._pending.pop(peer_id, None)
|
|
|
|
def _cleanup_expired(self):
|
|
now = time.monotonic()
|
|
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")
|