ForcePilot/backend/package/yuxi/channels/adapters/mattermost/interactions.py
Kris 002d601d1b feat(mattermost): 实现完整的 Mattermost 适配器模块
新增 Mattermost 渠道完整实现,包含适配器核心、消息处理、交互回调、命令支持、安全校验、多账号管理等功能,支持机器人消息发送、交互按钮、命令注册、投票功能以及配置动态修改等特性。
2026-05-12 00:46:12 +08:00

128 lines
3.4 KiB
Python

from __future__ import annotations
import hashlib
import hmac
import json
import re
from typing import Any
def sanitize_action_id(action_id: str) -> str:
return re.sub(r"[-_]", "", action_id)
def build_button_attachment(
title: str,
text: str,
buttons: list[dict[str, str]],
fallback: str = "",
color: str = "",
callback_id: str = "",
) -> dict[str, Any]:
"""构建 Mattermost Interactive Button attachment。"""
actions = []
for i, btn in enumerate(buttons):
btn_id = sanitize_action_id(btn.get("id", f"btn_{i}"))
actions.append(
{
"id": btn_id,
"name": btn.get("name", f"选择 {i + 1}"),
"integration": {
"url": btn.get("callback_url", ""),
"context": {
"action": btn.get("action", ""),
"button_id": btn_id,
"callback_id": sanitize_action_id(callback_id),
"value": btn.get("value", ""),
},
},
"type": "button",
"text": btn.get("text", f"Button {i + 1}"),
"style": btn.get("style", ""),
}
)
return {
"fallback": fallback or title,
"title": title,
"text": text,
"color": color or "",
"callback_id": callback_id,
"actions": actions,
}
def build_select_attachment(
title: str,
text: str,
options: list[dict[str, str]],
select_name: str = "selection",
callback_id: str = "",
) -> dict[str, Any]:
"""构建 Mattermost Select dropdown attachment。"""
select_options = []
for opt in options:
select_options.append(
{
"text": opt.get("text", ""),
"value": opt.get("value", ""),
}
)
return {
"fallback": title,
"title": title,
"text": text,
"callback_id": sanitize_action_id(callback_id),
"actions": [
{
"id": sanitize_action_id(select_name),
"name": select_name,
"integration": {
"url": "",
"context": {"action": select_name, "callback_id": sanitize_action_id(callback_id)},
},
"type": "select",
"options": select_options,
},
],
}
def parse_interaction_context(data: dict) -> dict:
"""解析交互回调 context。"""
context = data.get("context", {})
if isinstance(context, str):
try:
context = json.loads(context)
except json.JSONDecodeError:
context = {}
return context
def verify_hmac_signature(payload: bytes, signature: str, secret: str) -> bool:
"""验证 HMAC 签名。"""
if not signature or not secret:
return True
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)
def build_interactive_post_props(
channel_id: str,
message: str,
attachments: list[dict] | None = None,
root_id: str = "",
) -> dict[str, Any]:
"""构建带 interactive attachments 的 post 选项。"""
return {
"channel_id": channel_id,
"message": message,
"root_id": root_id,
"props": {"attachments": attachments or []},
}