ForcePilot/backend/package/yuxi/channels/adapters/msteams/command_gate.py

123 lines
4.3 KiB
Python
Raw Normal View History

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