ForcePilot/backend/package/yuxi/channels/adapters/msteams/command_gate.py
Kris bd60c15df0 feat(msteams): 新增完整的 Microsoft Teams 适配器模块
实现了 Teams 机器人所需的全功能组件,包括:
- 基础命令解析与帮助卡片生成
- 租户验证与访问控制
- 自定义 UA 与媒体工具
- 消息分块、批注处理与会话管理
- 防抖、缓存与配置路由能力
- 投票、配对、审计与运行时状态管理
- TTS 语音合成与卡片构建工具
- 群组管理与权限控制逻辑
2026-05-12 00:46:44 +08:00

123 lines
4.3 KiB
Python

"""Microsoft Teams 双重命令授权门控。
DM + Group allowlist 双重检查,确保命令仅在授权范围内执行。
支持 resolveDualTextControlCommandGate 双重命令门控。
"""
from __future__ import annotations
from typing import Any
from yuxi.utils.logging_config import logger
class CommandGate:
def __init__(self, config: dict[str, Any] | None = None):
config = config or {}
self._dm_allowed_commands: set[str] = set(config.get("dm_allowed_commands", []))
self._group_allowed_commands: set[str] = set(config.get("group_allowed_commands", []))
self._admin_user_ids: set[str] = set(config.get("admin_user_ids", []))
self._dm_blocked_commands: set[str] = set(config.get("dm_blocked_commands", []))
self._group_blocked_commands: set[str] = set(config.get("group_blocked_commands", []))
self._command_enabled: bool = config.get("command_enabled", True)
def is_command_authorized(
self,
command: str,
chat_type: str,
user_id: str = "",
) -> bool:
normalized = command.lower().strip("/")
if not self._command_enabled:
return False
if chat_type == "direct":
if normalized in self._dm_blocked_commands:
return False
if self._dm_allowed_commands:
return normalized in self._dm_allowed_commands
return True
if chat_type in ("group", "channel"):
if normalized in self._group_blocked_commands:
return False
if self._group_allowed_commands:
return normalized in self._group_allowed_commands
return True
return True
def is_admin(self, user_id: str) -> bool:
if not self._admin_user_ids:
return False
return user_id in self._admin_user_ids
def resolve_command_gate(
self,
config: dict[str, Any] | None,
command: str,
chat_type: str,
user_id: str = "",
) -> bool:
if config:
team_gate = CommandGate(config)
if team_gate._dm_allowed_commands or team_gate._group_allowed_commands:
return team_gate.is_command_authorized(command, chat_type, user_id)
return self.is_command_authorized(command, chat_type, user_id)
def resolve_dual_text_control_command_gate(
self,
dm_config: dict[str, Any] | None,
group_config: dict[str, Any] | None,
command: str,
chat_type: str,
user_id: str = "",
) -> bool:
"""DM + Group 双重命令门控。
分别解析 DM 和 Group 配置,执行双重检查。
按 (chat_type, command) 维度组合授权结果:
- DM 聊天使用 dm_config 回退到 self
- Group/Channel 聊天使用 group_config 回退到 self
"""
normalized = command.lower().strip("/")
dm_gate = CommandGate(dm_config) if dm_config else self
group_gate = CommandGate(group_config) if group_config else self
if chat_type == "direct":
result = dm_gate._check_command(normalized, "direct")
if not result:
logger.debug(f"MSTeams: DM command '{command}' denied by dual gate")
return result
if chat_type in ("group", "channel"):
result = group_gate._check_command(normalized, "group")
if not result:
logger.debug(f"MSTeams: Group command '{command}' denied by dual gate")
return result
return True
def _check_command(self, normalized_cmd: str, context: str) -> bool:
"""内部命令检查逻辑,供 resolveDualTextControlCommandGate 使用。"""
if not self._command_enabled:
return False
if context == "direct":
if normalized_cmd in self._dm_blocked_commands:
return False
if self._dm_allowed_commands:
return normalized_cmd in self._dm_allowed_commands
elif context in ("group",):
if normalized_cmd in self._group_blocked_commands:
return False
if self._group_allowed_commands:
return normalized_cmd in self._group_allowed_commands
return True
resolve_dual_text_control_command_gate = CommandGate.resolve_dual_text_control_command_gate