"""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