ForcePilot/backend/package/yuxi/channel/extensions/googlechat/access_policy.py
Kris c4e301a5e0 feat(googlechat): 新增Google Chat渠道完整实现
新增Google Chat插件的所有核心功能模块,包括账户配置、消息收发、权限控制、媒体处理、会话管理等完整能力
2026-05-21 10:49:33 +08:00

146 lines
4.8 KiB
Python

from __future__ import annotations
import re
import logging
logger = logging.getLogger(__name__)
_GROUP_KEY_PATTERN = re.compile(r"^spaces/.+$")
def apply_inbound_access_policy(
space_name: str,
space_type: str,
sender_name: str,
is_dm: bool,
account: dict,
) -> dict:
"""
Returns {"allowed": bool, "reason": str, "requires_pairing": bool}
"""
if is_dm:
return _check_dm_policy(sender_name, account)
else:
return _check_group_policy(space_name, space_type, account)
def _check_dm_policy(sender_name: str, account: dict) -> dict:
dm_policy = account.get("dm_policy", "pairing")
dm_enabled = account.get("dm_enabled", True)
if not dm_enabled:
return {"allowed": False, "reason": "dm_disabled"}
if dm_policy == "open":
return {"allowed": True, "reason": "open_dm"}
if dm_policy == "allowlist":
allow_from = account.get("dm_allow_from", [])
if "*" in allow_from:
return {"allowed": True, "reason": "wildcard_allow"}
if sender_name in allow_from:
return {"allowed": True, "reason": "in_allowlist"}
normalized = _normalize_sender_for_allowlist_check(sender_name, allow_from)
if normalized:
return {"allowed": True, "reason": "in_allowlist"}
return {"allowed": False, "reason": "not_in_allowlist"}
if dm_policy == "pairing":
allow_from = account.get("dm_allow_from", [])
resolved = account.get("resolved")
if resolved and hasattr(resolved, "dm_allow_from"):
allow_from = resolved.dm_allow_from
if sender_name in allow_from:
return {"allowed": True, "reason": "already_paired"}
normalized = _normalize_sender_for_allowlist_check(sender_name, allow_from)
if normalized:
return {"allowed": True, "reason": "already_paired"}
return {"allowed": False, "reason": "pairing_required", "requires_pairing": True}
return {"allowed": False, "reason": f"unknown_policy: {dm_policy}"}
def _check_group_policy(space_name: str, space_type: str, account: dict) -> dict:
group_policy = account.get("group_policy", "allowlist")
groups = account.get("groups", {})
if group_policy == "disabled":
return {"allowed": False, "reason": "groups_disabled"}
if group_policy == "open":
return {"allowed": True, "reason": "open_group"}
if group_policy == "allowlist":
if "*" in groups:
return {"allowed": True, "reason": "wildcard_group"}
if space_name in groups:
entry = groups[space_name]
if entry.get("enabled") is False:
return {"allowed": False, "reason": "group_disabled"}
return {"allowed": True, "reason": "in_group_list"}
if not _GROUP_KEY_PATTERN.match(space_name):
return {"allowed": False, "reason": "deprecated_group_key"}
return {"allowed": False, "reason": "not_in_group_list"}
return {"allowed": False, "reason": f"unknown_policy: {group_policy}"}
def _normalize_sender_for_allowlist_check(sender_name: str, allow_from: list[str]) -> str | None:
for entry in allow_from:
if entry.startswith("googlechat:"):
if entry[len("googlechat:"):] == sender_name:
return sender_name
if entry.startswith("users/"):
if entry == sender_name:
return sender_name
return None
def is_control_command_message(text: str) -> bool:
text = text.strip()
return text.startswith("/config") or text.startswith("/status") or text.startswith("/admin")
def should_require_command_authorization(
is_group: bool, text: str, command_authorized: bool
) -> bool:
if not is_group:
return False
if not is_control_command_message(text):
return False
return not command_authorized
def detect_mention_for_access(annotations: list[dict]) -> bool:
if not annotations:
return False
for ann in annotations:
if ann.get("type") == "USER_MENTION":
um = ann.get("userMention", {})
if um.get("user", {}).get("name") == "users/app":
return True
return False
def resolve_group_require_mention(space_name: str, account: dict) -> bool:
groups = account.get("groups", {})
default_require = account.get("require_mention", True)
if "*" in groups:
entry = groups["*"]
if "requireMention" in entry:
return entry["requireMention"]
if space_name in groups:
entry = groups[space_name]
if "requireMention" in entry:
return entry["requireMention"]
return default_require
def should_bypass_mention_for_text_commands(text: str, account: dict) -> bool:
if not account.get("allow_text_commands", False):
return False
return is_control_command_message(text)