ForcePilot/backend/package/yuxi/channel/extensions/freshdesk/security.py
Kris 3e861d6cd1 feat(freshdesk): 新增Freshdesk/Freshchat渠道插件
实现完整的Freshdesk和Freshchat集成支持,包含会话守卫、错误定义、消息去重、配置管理、webhook处理、出站消息发送、状态监控、安全校验、配对功能和流式回复支持
2026-05-21 10:47:20 +08:00

96 lines
3.4 KiB
Python

import logging
logger = logging.getLogger(__name__)
class FreshdeskSecurity:
def __init__(self, gateway):
self._gateway = gateway
def resolve_dm_policy(self) -> dict:
if self._gateway is None or not self._gateway.accounts:
return {"mode": "open", "allow_from": []}
entry = next(iter(self._gateway.accounts.values()), None)
if entry is None:
return {"mode": "open", "allow_from": []}
account = entry["account"]
return {
"mode": account.dm_policy,
"allow_from": account.allow_from,
}
async def check_allowlist(self, peer_id: str, channel_type: str) -> bool:
policy = self.resolve_dm_policy()
if policy["mode"] == "disabled":
return False
if policy["mode"] == "open":
allow_from = policy["allow_from"]
if "*" in allow_from or not allow_from:
return True
return peer_id in allow_from
if policy["mode"] == "allowlist":
allow_from = policy["allow_from"]
if not allow_from:
return False
return peer_id in allow_from
if policy["mode"] == "pairing":
return True
return False
def authorize_dm(self, account, contact_id: str) -> tuple[bool, str]:
policy = account.dm_policy if hasattr(account, 'dm_policy') else account.get("dm_policy", "open")
allow_from = account.allow_from if hasattr(account, 'allow_from') else account.get("allow_from", [])
if policy == "disabled":
return False, "DM is disabled"
if policy == "open":
if "*" in allow_from or not allow_from:
return True, ""
return contact_id in allow_from, "not-allowlisted"
if policy == "allowlist":
if not allow_from:
return False, "allowlist-empty"
return contact_id in allow_from, "not-allowlisted"
if policy == "pairing":
return True, "pairing-required"
return False, "unknown-policy"
def apply_config_fixes(self, config: dict) -> dict:
config.setdefault("channels", {})
config["channels"].setdefault("freshdesk", {})
return config
def collect_warnings(self, config: dict, account_id: str | None = None, account=None) -> list[str]:
warnings: list[str] = []
if account:
account_dict = account if isinstance(account, dict) else {
"dm_policy": getattr(account, "dm_policy", "open"),
"allow_from": getattr(account, "allow_from", []),
}
if not account_dict.get("webhook_secret"):
warnings.append("webhookSecret is not configured. Webhook signature verification will be skipped.")
if account_dict.get("dm_policy") == "open" and not account_dict.get("allow_from"):
warnings.append('dmPolicy is "open" without allowFrom. Consider adding allowFrom=["*"] explicitly.')
if account_dict.get("dm_policy") == "allowlist" and not account_dict.get("allow_from"):
warnings.append('dmPolicy is "allowlist" but allowFrom is empty. No contacts will be allowed.')
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 []