ForcePilot/backend/package/yuxi/channel/extensions/slack/security.py
Kris bfc7755137 feat(channel): 添加 Slack 渠道扩展
新增 Slack 渠道扩展,支持在 Yuxi 平台中集成 Slack 团队协作平台。

包含以下功能模块:
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- outbound: 外发消息管理
- streaming: 流式消息处理
- pairing: 用户配对与绑定
- security: 安全校验
- monitor: 渠道状态监控
- status: 会话状态管理
- actions: 交互动作处理
- interactive: 交互式消息
- commands: 斜杠指令
- threading: 线程管理
- mentions: @提及
- constants: 常量定义
- types: 类型定义
2026-05-21 11:43:23 +08:00

108 lines
3.5 KiB
Python

import logging
import re
from yuxi.channel.protocols import ChannelGroupContext
from yuxi.channel.extensions.slack.constants import (
SLACK_CHANNEL_ID_PATTERN,
SLACK_DM_ID_PATTERN,
SLACK_PRIVATE_CHANNEL_ID_PATTERN,
SLACK_USER_ID_PATTERN,
SLACK_WORKSPACE_ID_PATTERN,
)
logger = logging.getLogger(__name__)
_SLACK_ID_PATTERNS = [
re.compile(SLACK_USER_ID_PATTERN),
re.compile(SLACK_CHANNEL_ID_PATTERN),
re.compile(SLACK_PRIVATE_CHANNEL_ID_PATTERN),
re.compile(SLACK_DM_ID_PATTERN),
re.compile(SLACK_WORKSPACE_ID_PATTERN),
]
_USER_ID_RE = re.compile(SLACK_USER_ID_PATTERN)
_CHANNEL_ID_RE = re.compile(SLACK_CHANNEL_ID_PATTERN)
class SlackSecurity:
def resolve_dm_policy(self) -> dict:
return {"mode": "pairing", "allow_from": []}
async def check_allowlist(self, peer_id: str, channel_type: str) -> bool:
return True
def resolve_dm_policy_for_account(self, account: dict) -> dict:
mode = account.get("dm_policy", "pairing")
return {"mode": mode, "allow_from": account.get("allow_from", [])}
def resolve_group_policy_for_account(self, account: dict) -> dict:
mode = account.get("group_policy", "open")
return {"mode": mode, "group_allow_from": account.get("group_allow_from", [])}
def resolve_require_mention(self, ctx: ChannelGroupContext) -> bool | None:
if not ctx:
return True
config = ctx.config
group_id = ctx.group_id
if group_id:
channels_cfg = config.get("channels", {}).get("slack", {})
channel_cfg = channels_cfg.get(group_id, {})
if "requireMention" in channel_cfg:
return channel_cfg["requireMention"]
return True
def resolve_group_intro_hint(self, ctx: ChannelGroupContext) -> str | None:
return None
def resolve_tool_policy(self, ctx: ChannelGroupContext) -> dict | None:
return None
def apply_config_fixes(self, config: dict) -> dict:
config.setdefault("channels", {})
config["channels"].setdefault("slack", {})
return config
def collect_warnings(self, config: dict, account_id: str | None = None, account: dict | None = None) -> list[str]:
warnings = []
if account:
dm_policy = account.get("dm_policy", "")
group_policy = account.get("group_policy", "")
if dm_policy == "open" and not account.get("allow_from"):
warnings.append("dmPolicy is 'open' without allowFrom")
if group_policy == "open" and not account.get("group_allow_from"):
warnings.append("groupPolicy is 'open' without groupAllowFrom")
return warnings
def collect_audit_findings(
self,
config,
account_id=None,
account=None,
source_config=None,
ordered_account_ids=None,
has_explicit_account_path=False,
) -> list[dict]:
return []
def normalize_allow_entry(self, entry: str) -> str:
for prefix in ("slack:", "user:"):
if entry.lower().startswith(prefix):
entry = entry[len(prefix) :]
return entry.upper()
def is_valid_slack_id_format(self, id_str: str) -> bool:
return any(pattern.match(id_str) for pattern in _SLACK_ID_PATTERNS)
def is_valid_user_id(self, id_str: str) -> bool:
return bool(_USER_ID_RE.match(id_str))
def is_valid_channel_id(self, id_str: str) -> bool:
return bool(_CHANNEL_ID_RE.match(id_str))