ForcePilot/backend/package/yuxi/channels/adapters/mattermost/monitor.py
Kris 002d601d1b feat(mattermost): 实现完整的 Mattermost 适配器模块
新增 Mattermost 渠道完整实现,包含适配器核心、消息处理、交互回调、命令支持、安全校验、多账号管理等功能,支持机器人消息发送、交互按钮、命令注册、投票功能以及配置动态修改等特性。
2026-05-12 00:46:12 +08:00

132 lines
4.1 KiB
Python

from __future__ import annotations
from dataclasses import dataclass, field
ChatMode = str
MODE_ONCALL: ChatMode = "oncall"
MODE_ONMESSAGE: ChatMode = "onmessage"
MODE_ONCHAR: ChatMode = "onchar"
CHAT_MODES = frozenset({MODE_ONCALL, MODE_ONMESSAGE, MODE_ONCHAR})
DEFAULT_ONCHAR_PREFIXES = frozenset({">", "!"})
@dataclass
class MentionGateConfig:
chat_mode: ChatMode = MODE_ONMESSAGE
require_mention: bool = True
onchar_prefixes: set[str] = field(default_factory=lambda: set(DEFAULT_ONCHAR_PREFIXES))
@classmethod
def from_config(cls, config: dict) -> MentionGateConfig:
chat_mode = config.get("chatmode", MODE_ONMESSAGE)
if chat_mode not in CHAT_MODES:
chat_mode = MODE_ONMESSAGE
prefixes_raw = config.get("onchar_prefixes", [])
if not prefixes_raw:
prefixes_raw = list(DEFAULT_ONCHAR_PREFIXES)
prefixes = {str(p).strip() for p in prefixes_raw if str(p).strip()}
if not prefixes:
prefixes = set(DEFAULT_ONCHAR_PREFIXES)
require_mention = bool(config.get("require_mention", True))
return cls(
chat_mode=chat_mode,
require_mention=require_mention,
onchar_prefixes=frozenset(prefixes),
)
@dataclass
class MentionGateResult:
should_respond: bool
reason: str = ""
class MentionGate:
"""消息响应门控 — 决定是否响应入站消息。
支持三种模式:
- oncall: 仅响应明确呼叫(不自动触发)
- onmessage: 响应所有消息(结合 requireMention 过滤)
- onchar: 仅响应以特定前缀开头的消息
"""
def __init__(self, config: dict | None = None):
self._config = MentionGateConfig.from_config(config or {})
@property
def chat_mode(self) -> str:
return self._config.chat_mode
@property
def require_mention(self) -> bool:
return self._config.require_mention
def check(
self,
chat_type: str,
text: str,
bot_mentioned: bool,
) -> MentionGateResult:
mode = self._config.chat_mode
if chat_type == "direct":
return self._check_dm(text, mode)
return self._check_group_or_channel(text, bot_mentioned, mode)
def _check_dm(self, text: str, mode: ChatMode) -> MentionGateResult:
if mode == MODE_ONCALL:
return MentionGateResult(True)
if mode == MODE_ONCHAR:
if text and any(text.lstrip().startswith(p) for p in self._config.onchar_prefixes):
return MentionGateResult(True)
return MentionGateResult(False, "DM message does not match onchar prefix")
return MentionGateResult(True)
def _check_group_or_channel(self, text: str, bot_mentioned: bool, mode: ChatMode) -> MentionGateResult:
if mode == MODE_ONCALL:
return MentionGateResult(False, "oncall mode does not respond in groups without explicit mention")
if mode == MODE_ONMESSAGE:
if self._config.require_mention and not bot_mentioned:
return MentionGateResult(False, "Bot not mentioned and requireMention is enabled")
return MentionGateResult(True)
if mode == MODE_ONCHAR:
if text and any(text.lstrip().startswith(p) for p in self._config.onchar_prefixes):
return MentionGateResult(True)
return MentionGateResult(False, "Message does not match onchar prefix")
return MentionGateResult(False, f"Unknown chat mode: {mode}")
CONTROL_COMMAND_MARKERS = frozenset({"/restart", "/sudo", "/exec", "/delete", "/config", "/approve", "/deny"})
def is_control_command(text: str) -> bool:
text_stripped = text.strip()
return any(text_stripped.startswith(cmd) for cmd in CONTROL_COMMAND_MARKERS)
def resolve_control_command_gate(
text: str,
chat_type: str,
bot_mentioned: bool = False,
) -> bool:
"""控制命令门控 — 控制命令总是通过(仅 DM/mention 场景),
由后续的 authorize_command_invocation 做进一步授权检查。
"""
if not is_control_command(text):
return False
if chat_type == "direct":
return True
return bot_mentioned