123 lines
4.3 KiB
Python
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
|