完成Help Scout渠道的完整功能实现,包含API客户端、webhook处理、轮询同步、自动回复、工单标签分配、客户资料管理等核心能力,附带完整的配置Schema与插件元信息
98 lines
3.4 KiB
Python
98 lines
3.4 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
|
|
from yuxi.channel.extensions.helpscout.types import OutboundResult
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
FAQ_RULES: dict[str, dict] = {
|
|
"reset password": {
|
|
"reply": (
|
|
"To reset your password, please visit https://example.com/reset-password "
|
|
"and follow the instructions. If you need further assistance, please let us know."
|
|
),
|
|
"tags": ["account", "password"],
|
|
},
|
|
"cancel subscription": {
|
|
"reply": (
|
|
"To cancel your subscription, please go to your Account Settings > Billing "
|
|
"and click 'Cancel Subscription'. If you have any questions, we're here to help."
|
|
),
|
|
"tags": ["billing", "subscription"],
|
|
},
|
|
"contact support": {
|
|
"reply": (
|
|
"You can reach our support team by replying to this email or visiting "
|
|
"https://example.com/support. Our team is available Monday-Friday, 9 AM - 6 PM."
|
|
),
|
|
"tags": ["support"],
|
|
},
|
|
}
|
|
|
|
|
|
class HelpScoutAutoReply:
|
|
async def check_faq_match(self, body_text: str) -> dict | None:
|
|
text_lower = body_text.lower()
|
|
for keyword, faq in FAQ_RULES.items():
|
|
if keyword in text_lower:
|
|
logger.debug(
|
|
"Help Scout FAQ match: keyword=%s",
|
|
keyword,
|
|
)
|
|
return {"keyword": keyword, **faq}
|
|
return None
|
|
|
|
async def send_faq_reply(
|
|
self,
|
|
conversation_id: int,
|
|
reply_text: str,
|
|
tags: list[str] | None = None,
|
|
account_id: str | None = None,
|
|
) -> OutboundResult:
|
|
account = resolve_account(account_id or "default")
|
|
client = self._build_client(account)
|
|
|
|
try:
|
|
result = await client.reply_conversation(
|
|
conversation_id=conversation_id,
|
|
text=reply_text,
|
|
draft=True,
|
|
)
|
|
reply_id = str(result.get("id", ""))
|
|
|
|
if tags:
|
|
conv = await client.get_conversation(conversation_id)
|
|
existing_tags = conv.get("tags", [])
|
|
new_tags = [t for t in tags if t not in existing_tags]
|
|
if new_tags:
|
|
await client.update_conversation(
|
|
conversation_id,
|
|
{"tags": existing_tags + new_tags},
|
|
)
|
|
|
|
note = "Auto-reply draft generated from FAQ match. Review and send when ready."
|
|
await client.create_note(conversation_id=conversation_id, text=note)
|
|
|
|
logger.info(
|
|
"Help Scout FAQ auto-reply drafted: conv=%d",
|
|
conversation_id,
|
|
)
|
|
return OutboundResult(success=True, message_id=reply_id)
|
|
except Exception as e:
|
|
logger.exception(
|
|
"Help Scout FAQ auto-reply failed: conv=%d",
|
|
conversation_id,
|
|
)
|
|
return OutboundResult(success=False, error="exception", detail=str(e))
|
|
finally:
|
|
await client.close()
|
|
|
|
@staticmethod
|
|
def _build_client(account: dict) -> HelpScoutClient:
|
|
auth = HelpScoutAuth(account["app_id"], account["app_secret"])
|
|
return HelpScoutClient(auth)
|