该提交新增了完整的BlueBubbles渠道插件,支持通过BlueBubbles Server集成iMessage功能,包含以下核心能力: 1. 支持私聊和群聊会话管理,自动区分会话类型 2. 完整的消息收发支持,包括文本、图片、语音、文件、视频消息 3. 支持消息反应、已读回执、消息编辑与撤回 4. 内置去重、防抖处理机制 5. 支持Webhook和WebSocket两种事件接收方式 6. 完善的权限与安全校验机制 7. 历史消息同步与抓包功能 8. TTS语音合成与发送支持 9. 群组管理能力,包括重命名、修改头像、增减成员等
94 lines
2.9 KiB
Python
94 lines
2.9 KiB
Python
import hashlib
|
|
import hmac
|
|
import json
|
|
import logging
|
|
from http import HTTPStatus
|
|
|
|
from fastapi import APIRouter, Request, HTTPException, Response
|
|
|
|
from yuxi.channel.extensions.bluebubbles.monitor import process_inbound_message
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _constant_time_compare(a: str, b: str) -> bool:
|
|
return hmac.compare_digest(a.encode() if isinstance(a, str) else a, b.encode() if isinstance(b, str) else b)
|
|
|
|
|
|
class BlueBubblesWebhookHandler:
|
|
def __init__(
|
|
self,
|
|
webhook_secret: str = "",
|
|
account_id: str = "default",
|
|
dedupe_store=None,
|
|
debounce_manager=None,
|
|
on_message=None,
|
|
send_read_receipt=None,
|
|
):
|
|
self.webhook_secret = webhook_secret
|
|
self.account_id = account_id
|
|
self.dedupe_store = dedupe_store
|
|
self.debounce_manager = debounce_manager
|
|
self.on_message = on_message
|
|
self.send_read_receipt = send_read_receipt
|
|
|
|
async def handle_webhook(self, request: Request) -> Response:
|
|
if self.webhook_secret:
|
|
sig = request.headers.get("X-BB-Signature", "")
|
|
body = await request.body()
|
|
expected = hmac.new(
|
|
self.webhook_secret.encode(),
|
|
body,
|
|
hashlib.sha256,
|
|
).hexdigest()
|
|
if not _constant_time_compare(sig, expected):
|
|
raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail="Invalid signature")
|
|
|
|
try:
|
|
body = await request.body()
|
|
data = json.loads(body)
|
|
except json.JSONDecodeError:
|
|
raise HTTPException(status_code=HTTPStatus.BAD_REQUEST, detail="Invalid JSON")
|
|
|
|
if isinstance(data, list):
|
|
for item in data:
|
|
await process_inbound_message(
|
|
item,
|
|
self.account_id,
|
|
dedupe_store=self.dedupe_store,
|
|
debounce_manager=self.debounce_manager,
|
|
on_message=self.on_message,
|
|
send_read_receipt=self.send_read_receipt,
|
|
)
|
|
else:
|
|
await process_inbound_message(
|
|
data,
|
|
self.account_id,
|
|
dedupe_store=self.dedupe_store,
|
|
debounce_manager=self.debounce_manager,
|
|
on_message=self.on_message,
|
|
send_read_receipt=self.send_read_receipt,
|
|
)
|
|
|
|
return Response(status_code=HTTPStatus.OK)
|
|
|
|
|
|
def create_webhook_router(
|
|
path: str = "/bluebubbles-webhook",
|
|
webhook_secret: str = "",
|
|
account_id: str = "default",
|
|
**kwargs,
|
|
) -> APIRouter:
|
|
router = APIRouter()
|
|
handler = BlueBubblesWebhookHandler(
|
|
webhook_secret=webhook_secret,
|
|
account_id=account_id,
|
|
**kwargs,
|
|
)
|
|
|
|
@router.post(path)
|
|
async def _webhook(request: Request):
|
|
return await handler.handle_webhook(request)
|
|
|
|
return router
|