feat(trendradar): 新增空权限插件并接入webhook群权限校验
- 新建 trendradar_permission 空插件,仅注册 TRENDRADAR_WEBHOOK 功能用于后台群级开关 - webhook 发送前强制校验群权限,未开启群加入 blocked_groups 并拦截 - 更新对接文档,补充权限开关的启用步骤与返回字段说明
This commit is contained in:
@@ -14,6 +14,7 @@ from typing import Any, Dict, List, Tuple
|
||||
|
||||
from flask import Blueprint, current_app, jsonify, request
|
||||
from loguru import logger
|
||||
from utils.robot_cmd.robot_command import Feature, GroupBotManager, PermissionStatus
|
||||
|
||||
|
||||
# 独立 webhook 路由,避免和后台管理接口混在一起。
|
||||
@@ -140,6 +141,30 @@ def _build_wechat_text(title: str, content: str, payload: Dict[str, Any]) -> str
|
||||
return "\n".join(lines).strip()
|
||||
|
||||
|
||||
def _filter_groups_by_permission(groups: List[str]) -> Tuple[List[str], List[str]]:
|
||||
"""按 TrendRadar 功能权限过滤目标群。
|
||||
|
||||
返回:
|
||||
1. allowed_groups: 有权限可发送的群
|
||||
2. blocked_groups: 未开启权限被拦截的群
|
||||
"""
|
||||
feature = Feature.get_feature("TRENDRADAR_WEBHOOK")
|
||||
# 若功能尚未注册,按“全部拦截”处理,避免误发;同时给出明确告警。
|
||||
if not feature:
|
||||
logger.warning("[TrendRadarWebhook] 未发现 TRENDRADAR_WEBHOOK 功能注册,当前请求将被拦截")
|
||||
return [], list(groups)
|
||||
|
||||
allowed: List[str] = []
|
||||
blocked: List[str] = []
|
||||
for gid in groups:
|
||||
status = GroupBotManager.get_group_permission(gid, feature)
|
||||
if status == PermissionStatus.ENABLED:
|
||||
allowed.append(gid)
|
||||
else:
|
||||
blocked.append(gid)
|
||||
return allowed, blocked
|
||||
|
||||
|
||||
def _check_token(cfg: Dict[str, Any], payload: Dict[str, Any]) -> bool:
|
||||
"""校验 webhook token。"""
|
||||
expected = str(cfg.get("token", "") or "").strip()
|
||||
@@ -172,6 +197,15 @@ def trendradar_webhook():
|
||||
target_groups = _extract_target_groups(cfg, payload)
|
||||
if not target_groups:
|
||||
return jsonify({"success": False, "message": "未配置目标群"}), 400
|
||||
allowed_groups, blocked_groups = _filter_groups_by_permission(target_groups)
|
||||
if not allowed_groups:
|
||||
return jsonify(
|
||||
{
|
||||
"success": False,
|
||||
"message": "目标群未开启 TrendRadar webhook 权限",
|
||||
"blocked_groups": blocked_groups,
|
||||
}
|
||||
), 403
|
||||
|
||||
text = _build_wechat_text(title, content, payload)
|
||||
loop = _get_or_create_loop()
|
||||
@@ -183,7 +217,7 @@ def trendradar_webhook():
|
||||
await dashboard_server.client.send_text_message(group_id, text, "")
|
||||
|
||||
timeout_seconds = int(cfg.get("send_timeout_seconds", 20))
|
||||
for group_id in target_groups:
|
||||
for group_id in allowed_groups:
|
||||
try:
|
||||
fut = asyncio.run_coroutine_threadsafe(_send_once(group_id), loop)
|
||||
fut.result(timeout=max(timeout_seconds, 5))
|
||||
@@ -200,10 +234,10 @@ def trendradar_webhook():
|
||||
"success": len(failed_groups) == 0,
|
||||
"title": title,
|
||||
"sent_groups": sent_groups,
|
||||
"blocked_groups": blocked_groups,
|
||||
"failed_groups": failed_groups,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"[TrendRadarWebhook] 处理失败: {e}")
|
||||
return jsonify({"success": False, "message": str(e)}), 500
|
||||
|
||||
|
||||
Reference in New Issue
Block a user