ForcePilot/backend/package/yuxi/channel/extensions/helpscout/assigner.py
Kris 432d5db970 feat(helpscout): 新增Help Scout客服工单渠道插件
完成Help Scout渠道的完整功能实现,包含API客户端、webhook处理、轮询同步、自动回复、工单标签分配、客户资料管理等核心能力,附带完整的配置Schema与插件元信息
2026-05-21 10:49:56 +08:00

82 lines
2.8 KiB
Python

from __future__ import annotations
import logging
from yuxi.channel.extensions.helpscout.auth import HelpScoutAuth
from yuxi.channel.extensions.helpscout.client import HelpScoutClient
from yuxi.channel.extensions.helpscout.config import resolve_account
logger = logging.getLogger(__name__)
CATEGORY_KEYWORDS: dict[str, list[str]] = {
"billing": ["refund", "invoice", "payment", "charge", "subscription", "billing"],
"technical": ["bug", "error", "crash", "not working", "broken", "issue", "problem"],
"account": ["login", "password", "access", "account", "sign in", "sign up"],
"general": ["question", "help", "how to", "what is", "inquiry"],
}
ASSIGNEE_RULES: dict[str, str] = {
"billing": "billing",
"technical": "engineering",
"account": "support",
"general": "support",
}
class HelpScoutAssigner:
async def auto_assign(
self,
conversation_id: int,
body_text: str,
account_id: str | None = None,
) -> int | None:
account = resolve_account(account_id or "default")
category = self._classify_conversation(body_text)
team_name = ASSIGNEE_RULES.get(category)
logger.debug(
"Help Scout auto-assign: conv=%d category=%s team=%s",
conversation_id,
category,
team_name,
)
if not team_name:
return None
client = self._build_client(account)
try:
teams_resp = await client.list_teams(mailbox=account["mailbox_id"])
teams = teams_resp.get("_embedded", {}).get("teams", [])
matched_team = next((t for t in teams if t.get("name", "").lower() == team_name), None)
if matched_team:
await client.update_conversation(conversation_id, {"assignTo": matched_team["id"]})
logger.info(
"Help Scout assigned: conv=%d team=%s(%d)",
conversation_id,
team_name,
matched_team["id"],
)
return matched_team["id"]
logger.debug("Help Scout team not found: %s for conv=%d", team_name, conversation_id)
return None
except Exception:
logger.exception("Help Scout auto-assign failed: conv=%d", conversation_id)
return None
finally:
await client.close()
@staticmethod
def _classify_conversation(body_text: str) -> str:
text_lower = body_text.lower()
for category, keywords in CATEGORY_KEYWORDS.items():
for keyword in keywords:
if keyword in text_lower:
return category
return "general"
@staticmethod
def _build_client(account: dict) -> HelpScoutClient:
auth = HelpScoutAuth(account["app_id"], account["app_secret"])
return HelpScoutClient(auth)