ForcePilot/backend/package/yuxi/channel/extensions/intercom/handoff.py
Kris 2ddcf1cc55 feat(channel): 添加 Intercom 渠道扩展
新增 Intercom 渠道扩展,支持在 Yuxi 平台中集成 Intercom 客服渠道。

包含以下功能模块:
- client: Intercom API 客户端封装
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- webhook: Webhook 事件处理
- streaming: 流式消息处理
- format: 消息格式转换
- outbound: 外发消息管理
- handoff: 转人工会话切换
- pairing: 用户配对与绑定
- security: 安全签名校验
- dedupe: 消息去重
- monitor: 渠道状态监控
- status: 会话状态管理
- session_guard: 会话防护
- types: 类型定义
2026-05-21 11:01:49 +08:00

55 lines
2.1 KiB
Python

import logging
from yuxi.channel.extensions.intercom.client import IntercomClient
logger = logging.getLogger(__name__)
class HandoffDetector:
@staticmethod
async def is_human_takeover(client: IntercomClient, conversation_id: str, bot_admin_id: str) -> bool:
try:
conv = await client.get_conversation(conversation_id)
except Exception:
logger.warning("Failed to fetch conversation %s for handoff check", conversation_id, exc_info=True)
return False
assignee = conv.get("assignee") or {}
if assignee and assignee.get("type") == "admin":
admin_id = str(assignee.get("id", ""))
if admin_id and admin_id != bot_admin_id:
return True
return False
@staticmethod
async def check_recent_admin_reply(client: IntercomClient, conversation_id: str, bot_admin_id: str) -> bool:
try:
parts = await client.get_conversation_parts(conversation_id)
except Exception:
logger.warning("Failed to fetch conversation parts for handoff check", exc_info=True)
return False
for part in reversed(parts):
author = part.get("author", {})
if author.get("type") == "admin" and str(author.get("id", "")) != bot_admin_id:
return True
if part.get("part_type") == "comment" and author.get("type") in ("contact", "user"):
break
return False
@staticmethod
async def get_last_admin_reply_time(client: IntercomClient | None, conversation_id: str, bot_admin_id: str) -> float | None:
if client is None:
return None
try:
parts = await client.get_conversation_parts(conversation_id)
except Exception:
return None
for part in reversed(parts):
author = part.get("author", {})
if author.get("type") == "admin" and str(author.get("id", "")) != bot_admin_id:
created_at = part.get("created_at", 0)
return float(created_at) if created_at else None
return None