新增 RocketChat 渠道扩展,支持在 Yuxi 平台中集成 RocketChat 团队协作平台。 包含以下功能模块: - client: RocketChat API 客户端封装 - config: 渠道配置管理 - gateway: SSE/WebSocket 网关接入 - websocket: WebSocket 实时连接 - webhook: Webhook 事件处理 - outbound: 外发消息管理 - streaming: 流式消息处理 - pairing: 用户配对与绑定 - security: 安全校验 - dedup: 消息去重 - monitor: 渠道状态监控 - status: 会话状态管理 - session: 会话管理 - gating: 门控管理 - threading: 线程管理 - reactions: 表情反应 - types: 类型定义
68 lines
1.8 KiB
Python
68 lines
1.8 KiB
Python
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import hmac
|
|
import json
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def verify_rocketchat_webhook_signature(
|
|
payload: bytes,
|
|
signature_header: str,
|
|
secret: str,
|
|
) -> bool:
|
|
if not signature_header or not secret:
|
|
return False
|
|
expected = hmac.new(
|
|
secret.encode("utf-8"),
|
|
payload,
|
|
hashlib.sha256,
|
|
).hexdigest()
|
|
computed = signature_header.replace("sha256=", "")
|
|
return hmac.compare_digest(computed, expected)
|
|
|
|
|
|
def parse_rocketchat_incoming_webhook(body: dict) -> dict | None:
|
|
channel = body.get("channel", body.get("channel_id", ""))
|
|
text = body.get("text", body.get("payload", {}).get("text", ""))
|
|
username = body.get("username", body.get("user_name", ""))
|
|
if not channel or not text:
|
|
return None
|
|
return {
|
|
"channel": channel,
|
|
"text": text,
|
|
"username": username,
|
|
"icon_emoji": body.get("icon_emoji", ""),
|
|
"attachments": body.get("attachments", []),
|
|
"raw": body,
|
|
}
|
|
|
|
|
|
async def handle_rocketchat_webhook(
|
|
request_body: bytes,
|
|
headers: dict,
|
|
webhook_secret: str = "",
|
|
) -> dict | None:
|
|
try:
|
|
body = json.loads(request_body.decode("utf-8"))
|
|
except (json.JSONDecodeError, UnicodeDecodeError):
|
|
body = {}
|
|
|
|
signature = headers.get("X-RocketChat-Signature", "")
|
|
if webhook_secret and not verify_rocketchat_webhook_signature(request_body, signature, webhook_secret):
|
|
logger.warning("Rocket.Chat webhook signature verification failed")
|
|
return None
|
|
|
|
parsed = parse_rocketchat_incoming_webhook(body)
|
|
if not parsed:
|
|
return None
|
|
|
|
logger.info(
|
|
"Rocket.Chat webhook received from %s in channel %s",
|
|
parsed.get("username", "unknown"),
|
|
parsed.get("channel", "unknown"),
|
|
)
|
|
return parsed
|