完成Help Scout渠道的完整功能实现,包含API客户端、webhook处理、轮询同步、自动回复、工单标签分配、客户资料管理等核心能力,附带完整的配置Schema与插件元信息
183 lines
6.3 KiB
Python
183 lines
6.3 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def parse_webhook_event_to_unified(event_type: str, payload: dict, account: dict) -> dict | None:
|
|
conv = payload.get("conversation", {})
|
|
|
|
if event_type == "convo.agent.reply.created":
|
|
logger.debug("Agent reply created: conv=%s — skipped", conv.get("id"))
|
|
return None
|
|
|
|
if event_type == "convo.ai-answers.created":
|
|
logger.debug("AI Answers conversation created: conv=%s — skipped", conv.get("id"))
|
|
return None
|
|
|
|
if event_type == "convo.status":
|
|
return _build_status_event(conv)
|
|
|
|
if event_type == "convo.created":
|
|
return _build_convo_created_event(conv, account)
|
|
|
|
if event_type == "convo.customer.reply.created":
|
|
return _build_customer_reply_event(conv, account)
|
|
|
|
if event_type == "convo.note.created":
|
|
return _build_note_event(conv, account)
|
|
|
|
if event_type in ("beacon.chat.created", "beacon.chat.customer.replied"):
|
|
return _build_beacon_chat_event(conv, account, event_type)
|
|
|
|
if event_type == "satisfaction.ratings":
|
|
return _build_satisfaction_event(payload, account)
|
|
|
|
if event_type == "convo.assigned":
|
|
return _build_assigned_event(payload)
|
|
|
|
if event_type == "convo.tags":
|
|
return _build_tags_event(payload)
|
|
|
|
if event_type == "customer.created":
|
|
return _build_customer_event(payload, action="created")
|
|
|
|
if event_type == "customer.updated":
|
|
return _build_customer_event(payload, action="updated")
|
|
|
|
logger.debug("Unhandled webhook event type: %s", event_type)
|
|
return None
|
|
|
|
|
|
def _build_convo_created_event(conv: dict, account: dict) -> dict:
|
|
customer = conv.get("primaryCustomer", {})
|
|
return {
|
|
"channel": "helpscout",
|
|
"dedup_key": f"conv:{conv['id']}:created",
|
|
"event_type": "convo.created",
|
|
"conversation_id": str(conv["id"]),
|
|
"conversation_status": conv.get("status", "active"),
|
|
"conversation_subject": conv.get("subject", ""),
|
|
"sender": {
|
|
"id": str(customer.get("id", "")),
|
|
"type": "customer",
|
|
"email": customer.get("email", ""),
|
|
},
|
|
"mailbox_id": str(conv.get("mailboxId", account.get("mailbox_id"))),
|
|
"timestamp": conv.get("createdAt", ""),
|
|
}
|
|
|
|
|
|
def _build_customer_reply_event(conv: dict, account: dict) -> dict:
|
|
return {
|
|
"channel": "helpscout",
|
|
"dedup_key": f"conv:{conv['id']}:reply:{conv.get('threadId', '')}",
|
|
"event_type": "convo.customer.reply.created",
|
|
"conversation_id": str(conv["id"]),
|
|
"conversation_status": conv.get("status", "active"),
|
|
"conversation_subject": conv.get("subject", ""),
|
|
"sender": {
|
|
"id": str(conv.get("customer", {}).get("id", "")),
|
|
"type": "customer",
|
|
"email": conv.get("customer", {}).get("email", ""),
|
|
},
|
|
"mailbox_id": str(conv.get("mailboxId", account.get("mailbox_id"))),
|
|
"text": conv.get("body", "") or conv.get("preview", ""),
|
|
"timestamp": conv.get("createdAt", ""),
|
|
}
|
|
|
|
|
|
def _build_note_event(conv: dict, account: dict) -> dict | None:
|
|
return {
|
|
"channel": "helpscout",
|
|
"dedup_key": f"conv:{conv['id']}:note:{conv.get('threadId', '')}",
|
|
"event_type": "convo.note.created",
|
|
"conversation_id": str(conv["id"]),
|
|
"note_text": conv.get("body", ""),
|
|
"is_internal": True,
|
|
"timestamp": conv.get("createdAt", ""),
|
|
}
|
|
|
|
|
|
def _build_status_event(conv: dict) -> dict:
|
|
return {
|
|
"channel": "helpscout",
|
|
"dedup_key": f"conv:{conv['id']}:status:{conv.get('status', '')}",
|
|
"event_type": "convo.status",
|
|
"conversation_id": str(conv["id"]),
|
|
"new_status": conv.get("status", ""),
|
|
"timestamp": conv.get("updatedAt", ""),
|
|
}
|
|
|
|
|
|
def _build_beacon_chat_event(conv: dict, account: dict, event_type: str) -> dict:
|
|
return {
|
|
"channel": "helpscout",
|
|
"dedup_key": f"beacon:{conv['id']}:{event_type}",
|
|
"event_type": event_type,
|
|
"conversation_id": str(conv["id"]),
|
|
"conversation_type": "chat",
|
|
"sender": {
|
|
"id": str(conv.get("customer", {}).get("id", "")),
|
|
"type": "customer",
|
|
"email": conv.get("customer", {}).get("email", ""),
|
|
},
|
|
"text": conv.get("body", "") or conv.get("preview", ""),
|
|
"timestamp": conv.get("createdAt", ""),
|
|
}
|
|
|
|
|
|
def _build_satisfaction_event(payload: dict, account: dict) -> dict:
|
|
rating = payload.get("rating", {})
|
|
return {
|
|
"channel": "helpscout",
|
|
"dedup_key": f"satisfaction:{rating.get('id', '')}",
|
|
"event_type": "satisfaction.ratings",
|
|
"conversation_id": str(rating.get("conversationId", "")),
|
|
"rating": rating.get("rating", 0),
|
|
"feedback": rating.get("feedback", ""),
|
|
"timestamp": rating.get("createdAt", ""),
|
|
}
|
|
|
|
|
|
def _build_assigned_event(payload: dict) -> dict:
|
|
conv = payload.get("conversation", {})
|
|
assignee = payload.get("assignee", {})
|
|
return {
|
|
"channel": "helpscout",
|
|
"dedup_key": f"conv:{conv.get('id')}:assigned:{assignee.get('id')}",
|
|
"event_type": "convo.assigned",
|
|
"conversation_id": str(conv.get("id", "")),
|
|
"assignee_id": str(assignee.get("id", "")),
|
|
"assignee_type": assignee.get("type", ""),
|
|
"timestamp": conv.get("updatedAt", ""),
|
|
}
|
|
|
|
|
|
def _build_tags_event(payload: dict) -> dict:
|
|
conv = payload.get("conversation", {})
|
|
tags = payload.get("tags", [])
|
|
return {
|
|
"channel": "helpscout",
|
|
"dedup_key": f"conv:{conv.get('id')}:tags",
|
|
"event_type": "convo.tags",
|
|
"conversation_id": str(conv.get("id", "")),
|
|
"tags": tags,
|
|
"timestamp": conv.get("updatedAt", ""),
|
|
}
|
|
|
|
|
|
def _build_customer_event(payload: dict, action: str) -> dict:
|
|
customer = payload.get("customer", {})
|
|
return {
|
|
"channel": "helpscout",
|
|
"dedup_key": f"customer:{customer.get('id')}:{action}",
|
|
"event_type": f"customer.{action}",
|
|
"customer_id": str(customer.get("id", "")),
|
|
"email": customer.get("email", ""),
|
|
"first_name": customer.get("firstName", ""),
|
|
"last_name": customer.get("lastName", ""),
|
|
"timestamp": customer.get(f"{'created' if action == 'created' else 'updated'}At", ""),
|
|
}
|