Files
gochat/docs/requirements/M08-notification-and-webhook.md
T
rogee 0dabb8cfa5 docs: 整理文档目录结构 — 清理过时文档、归集功能子目录、统一命名规范
清理:
- 删除 34 份过时文档(gap reports/QA临时报告/验收报告/阶段性文档)
- 删除 docs/.hermes/skills 第三方 skills 副本(16 文件)
- 删除 skills-lock.json

目录归集:
- 根目录仅保留 README.md 索引
- product/ — 产品与架构设计(PRD + ARCHITECTURE + P2设计文档 + AI/企业路线图)
- tracking/ — Chatwoot parity 开发跟踪
- requirements/ — M01-M12 模块需求
- plans/ — 历史实现计划
- parity/ — 路由 parity 与前端契约
- qa/ — QA 报告与测试计划
- ops/ — 运维部署

命名规范:
- 全小写 kebab-case,禁止全大写文件名
- product/tracking/ops 用 NN- 序号前缀
- requirements 用 MNN- 两位零填充模块号
- plans/qa 用 YYYY-MM-DD- 日期前缀
- requirements M1-M9 零填充为 M01-M09(修复字典序)

同步更新:
- backend/cmd/route_parity/main.go 路径默认值
- backend/scripts/parity_frontend_smoke.sh 报告路径
- 所有 docs 内部交叉引用
- .gitignore 排除编译产物 (backend/gochat, backend/route_parity)
- 新增迁移 000052/000053
- 前端 WS 相关修改
2026-07-09 14:53:27 +08:00

32 KiB
Raw Blame History

M8 通知与Webhook — Chatwoot 功能梳理文档

参照仓库:chatwoot-reference
产出日期:2026-05-22
版本基准:Chatwoot v3.x


目录

  1. 通知模型与类型
  2. 通知设置(NotificationSetting
  3. 通知订阅(NotificationSubscription / Push订阅)
  4. 通知生成与投递流程
  5. NotificationListener — 事件驱动的通知创建
  6. ActionCable实时推送
  7. Push通知(浏览器Push + FCM
  8. Email通知
  9. 通知Snooze与去重
  10. WebhookAccount Webhook
  11. IntegrationHook(集成Hook
  12. WebhookListener — 事件驱动的Webhook投递
  13. HookListener — 集成Hook执行
  14. InstallationWebhook(平台级事件推送)
  15. 事件分发体系(Dispatcher架构)

1. 通知模型与类型

功能描述

Notification 是 Chatwoot 应用内通知的核心数据模型,记录针对特定用户的通知条目。每个通知绑定到一个 primary_actor(目前仅 Conversation)和一个可选的 secondary_actor(如 Message 或 User),通过 polymorphic 关联实现。通知类型通过 enum 定义,涵盖对话创建、分配、提及、新消息、SLA违规等场景。

用户操作流程

  1. 坐席登录后在侧边通知面板看到未读通知列表
  2. 点击某条通知可标记为已读,跳转至对应对话
  3. 可批量标记全部已读、删除单条通知、删除全部/已读通知
  4. 可对通知进行" snooze "(延迟提醒),到期后自动恢复为未读
  5. 通知计数(unread_count + count)实时更新

涉及的API端点

方法 路径 说明
GET /api/v1/accounts/{account_id}/notifications 获取通知列表(支持分页、过滤 read/snoozed
PATCH /api/v1/accounts/{account_id}/notifications/{id} 标记单条通知已读
POST /api/v1/accounts/{account_id}/notifications/{id}/unread 标记通知为未读
POST /api/v1/accounts/{account_id}/notifications/{id}/snooze snooze 通知
DELETE /api/v1/accounts/{account_id}/notifications/{id} 删除单条通知
POST /api/v1/accounts/{account_id}/notifications/read_all 全部标记已读(可按 primary_actor 过滤)
POST /api/v1/accounts/{account_id}/notifications/destroy_all 删除全部/已读通知
GET /api/v1/accounts/{account_id}/notifications/unread_count 获取未读通知数

涉及的数据模型 + 关键字段

  • Notificationnotifications 表):
    • id (bigint, PK)
    • notification_type (integer, not null) — enum 见下表
    • primary_actor_type / primary_actor_id (string + bigint, polymorphic, not null)
    • secondary_actor_type / secondary_actor_id (string + bigint, polymorphic, optional)
    • user_id (bigint, not null, FK) — 通知目标用户
    • account_id (bigint, not null, FK)
    • read_at (datetime) — 已读时间
    • snoozed_until (datetime) — snooze到期时间
    • last_activity_at (datetime) — 最后活动时间
    • meta (jsonb) — 附加元数据
    • created_at, updated_at
  • NOTIFICATION_TYPES enum
类型 说明
conversation_creation 1 新对话创建
conversation_assignment 2 对话被分配给坐席
assigned_conversation_new_message 3 被分配的对话收到新消息
conversation_mention 4 对话中被 @提及
participating_conversation_new_message 5 参与中的对话收到新消息
sla_missed_first_response 6 SLA首次响应违规
sla_missed_next_response 7 SLA后续响应违规
sla_missed_resolution 8 SLA解决时间违规
  • PRIMARY_ACTORS 目前仅允许 Conversation
  • 性能索引:idx_notifications_performance (user_id, account_id, snoozed_until, read_at)
  • 唯一索引:uniq_primary_actor_per_account_notifications, uniq_secondary_actor_per_account_notifications

涉及的业务逻辑

  • NotificationBuilderapp/builders/notification_builder.rb):
    • 构建通知前检查:user_subscribed_to_notification?、blocked contact 过滤、user_can_access_conversation?(权限验证)
    • secondary_actor 默认为 Current.user
    • conversation_creation 类型需订阅确认才创建
  • NotificationFinderapp/finders/notification_finder.rb):
    • 分页查询:RESULTS_PER_PAGE = 15
    • 支持过滤:snoozed / read 两种状态(通过 includes 参数)
    • 提供 unread_countcount
    • 默认按 last_activity_at 降序排列
  • Notification::MarkConversationReadService:批量将某对话的未读通知标记为已读
  • Notification::DeleteNotificationJob:异步批量删除全部或已读通知(type: :all / :read

涉及的自动化/规则/事件

  • before_create :set_last_activity_at — 创建时设置活动时间
  • after_create_commit :process_notification_delivery, :dispatch_create_event — 创建后触发 Push/Email 投递 + ActionCable 事件
  • after_update_commit :dispatch_update_event — 更新后触发 ActionCable 事件
  • after_destroy_commit :dispatch_destroy_event — 删除后触发 ActionCable 事件(传序列化数据避免反序列化错误)
  • Notification::RemoveDuplicateNotificationJob — 创建后去重(保留最新,删除同一 user+primary_actor 的旧通知)

2. 通知设置(NotificationSetting

功能描述

NotificationSetting 是每个用户在每个账户下的通知偏好配置,使用 FlagShihTzu 位运算存储 email 和 push 两种投递渠道的开关。每种通知类型(conversation_creation, conversation_assignment 等)都可以独立开启/关闭 email 和 push 投递。

用户操作流程

  1. 坐席进入 Profile → Notification Preferences
  2. 对每种通知类型分别勾选是否接收 Email 通知和 Push 通知
  3. 保存后即时生效

涉及的API端点

方法 路径 说明
GET /api/v1/accounts/{account_id}/notification_settings 获取当前用户在该账户的通知设置
PATCH /api/v1/accounts/{account_id}/notification_settings 更新通知设置
  • 请求参数:{ notification_settings: { selected_email_flags: [...], selected_push_flags: [...] } }

涉及的数据模型 + 关键字段

  • NotificationSettingnotification_settings 表):
    • id (bigint, PK)
    • email_flags (integer, default 0) — 位运算存储每种通知类型的 email 开关
    • push_flags (integer, default 0) — 位运算存储每种通知类型的 push 开关
    • account_id (integer, FK)
    • user_id (integer, FK)
    • created_at, updated_at
  • 唯一索引:by_account_user (account_id, user_id)
  • EMAIL_NOTIFICATION_FLAGS:从 Notification::NOTIFICATION_TYPES 派生,格式 email_conversation_creation, email_conversation_assignment
  • PUSH_NOTIFICATION_FLAGS:同理,格式 push_conversation_creation
  • 位运算查询模式:flag_query_mode: :bit_operator

涉及的业务逻辑

  • NotificationSettingsController#update
    • 通过 selected_email_flagsselected_push_flags 数组参数更新位标志
    • 使用 FlagShihTzu 的 selected_*_flags= 方法批量设置

3. 通知订阅(NotificationSubscription / Push订阅)

功能描述

NotificationSubscription 记录用户订阅 Push 通知的设备/浏览器信息,支持 browser_pushWeb Push API)和 fcmFirebase Cloud Messaging)两种类型。每个订阅有唯一 identifier(浏览器 endpoint 或 FCM device_id),用于防止重复订阅和跨账户迁移。

用户操作流程

  1. 坐席在浏览器中授权 Push 通知权限
  2. 前端生成 Push subscription 对象(含 endpoint, p256dh, auth
  3. 调用 API 创建 NotificationSubscription
  4. 如果同一浏览器已登录多个账户,系统自动将订阅迁移到当前用户

涉及的API端点

方法 路径 说明
POST /api/v1/notification_subscriptions 创建/更新 Push 订阅
DELETE /api/v1/notification_subscriptions 删除 Push 订阅(按 push_token
  • 创建请求:{ notification_subscription: { subscription_type: 'browser_push'/'fcm', subscription_attributes: { endpoint, p256dh, auth } / { device_id, push_token } } }

涉及的数据模型 + 关键字段

  • NotificationSubscriptionnotification_subscriptions 表):
    • id (bigint, PK)
    • subscription_type (integer, not null) — enum: browser_push(1), fcm(2)
    • identifier (text) — 唯一标识(browser endpoint 或 FCM device_id
    • subscription_attributes (jsonb, not null) — 包含 endpoint, keys(p256dh, auth) 或 device_id, push_token 等
    • user_id (bigint, not null, FK)
    • created_at, updated_at
  • 唯一索引:index_notification_subscriptions_on_identifier

涉及的业务逻辑

  • NotificationSubscriptionBuilder
    • 根据 subscription_type 从 subscription_attributes 中提取 identifier
    • 如果 identifier 已存在且属于不同用户,自动迁移(move_subscription_to_user
    • 如果 identifier 已存在且属于同一用户,更新订阅信息
    • 否则新建订阅

4. 通知生成与投递流程

功能描述

完整的通知生命周期:事件触发 → NotificationListener 捕获 → NotificationBuilder 创建 Notification → after_create_commit 触发投递 → Push/Email 双通道发送 → ActionCable 实时推送前端更新。

通知投递流程图

事件(如 conversation.created
  → Dispatcher.dispatch(event_name, timestamp, data)
    → SyncDispatcher → ActionCableListener(实时推送)
    → AsyncDispatcher → EventDispatcherJob → NotificationListener
      → NotificationBuilder.perform → 创建 Notification
        → after_create_commit:
          1. process_notification_delivery:
             - PushNotificationJob → PushNotificationService → browser_push / fcm / ChatwootHub
             - EmailNotificationJob → EmailNotificationService → ConversationNotificationsMailer
             - RemoveDuplicateNotificationJob → 去重
          2. dispatch_create_event → SyncDispatcher → ActionCableListener.notification_created

投递条件检查

  • Push 投递条件:用户订阅了 push_{notification_type}? → 检查 notification_setting.push_flags
  • Email 投递条件
    1. 用户订阅了 email_{notification_type}? → 检查 notification_setting.email_flags
    2. notification.read_at 为 nil(尚未已读则发邮件)
    3. user.confirmed_at 不为 nil(邮箱已确认)
    4. account.within_email_rate_limit?(不超过每日发送限额)

5. NotificationListener — 事件驱动的通知创建

功能描述

NotificationListener 是异步事件监听器,订阅 AsyncDispatcher 中的事件,根据事件类型调用 NotificationBuilder 创建通知。监听以下事件:

监听事件列表

事件方法 事件名 通知逻辑
conversation_created conversation.created 对 inbox 所有成员创建 conversation_creation 通知
conversation_bot_handoff conversation.bot_handoff bot转人工时同上逻辑
assignee_changed assignee.changed 为 assignee 创建 conversation_assignment 通知(需 notifiable_assignee_change 且 assignee 不为空)
message_created message.created 调用 MentionService + NewMessageNotificationService

消息创建事件的子逻辑

  • Messages::MentionService
    • 仅处理 private 消息且包含 @提及 的内容
    • 解析 mention://user/{id}mention://team/{id} 格式
    • 团队提及会展开为所有团队成员的 user_ids
    • 过滤不在 inbox 成员/管理员中的提及用户
    • 为每个被提及用户创建 conversation_mention 通知
    • 同时将提及用户添加为对话参与者
  • Messages::NewMessageNotificationService
    • 仅处理 message.notifiable? 的消息
    • 为对话 assignee 创建 assigned_conversation_new_message 通知(排除 sender 本身)
    • 为对话参与者创建 participating_conversation_new_message 通知
    • 去重检查:already_notified? 防止同一条消息重复通知同一用户

6. ActionCable实时推送

功能描述

ActionCable 是 Chatwoot 实时通信的核心机制,通过 WebSocket 将各类事件推送至前端。SyncDispatcher 中的 ActionCableListener 在事件发生时同步广播,通过 ActionCableBroadcastJob 异步执行实际广播。

用户操作流程

  1. 前端通过 WebSocket 连接到 RoomChannel(参数:pubsub_token, user_id
  2. 连接成功后,stream_from 用户的 pubsub_token 和账户级 "account_{id}" 频道
  3. 前端 BaseActionCableConnector.js 监听各类事件并更新 Vuex/Pinia store

ActionCableListener 监听事件

事件方法 事件名 广播目标 推送数据
notification_created notification.created 通知所属用户的 pubsub_token notification.push_event_data + unread_count + count
notification_updated notification.updated 同上 同上
notification_deleted notification.deleted 同上 {id} + unread_count + count
account_cache_invalidated account.cache_invalidated 账户所有 agents 的 tokens cache_keys
message_created message.created inbox members + admins + contact_inbox message.push_event_data
message_updated message.updated 同上 同上
conversation_created conversation.created inbox members + admins conversation.push_event_data
conversation_updated conversation.updated 同上 同上
assignee_changed assignee.changed inbox members conversation.push_event_data
team_changed team.changed 同上 同上
conversation_contact_changed conversation.contact_changed 同上 同上
conversation_typing_on conversation.typing_on inbox members + contact_inbox (排除当前 typing 用户) conversation + user push_event_data
conversation_typing_off conversation.typing_off 同上 同上
contact_created/updated/deleted contact.* account_{id} 频道 contact push_event_data
conversation_mentioned conversation.mentioned 被提及用户的 token conversation push_event_data

广播机制

  • broadcast(account, tokens, event_name, data) 方法:
    • tokens 为 pubsub_token 数组(包含用户 token 和账户级 token
    • 通过 ActionCableBroadcastJob.perform_later 异步广播
    • 对 CONVERSATION_UPDATE_EVENTS 类事件,会重新查询最新数据避免前端乱序
  • RoomChannel
    • 连接验证:通过 pubsub_token + user_id 找到合法用户
    • stream_from 用户 pubsub_token + "account_{account_id}" 频道
    • 订阅时更新 OnlineStatusTracker(在线状态追踪)
  • Pubsubable concern:User 模型包含,自动生成 pubsub_token,密码修改时轮换 token

7. Push通知(浏览器Push + FCM

功能描述

Push 通知是通知到达用户设备的通道,支持三种方式:浏览器 Web PushVAPID)、Firebase Cloud MessagingFCM)和 ChatwootHub 云端推送。PushNotificationService 根据用户的通知订阅逐一尝试推送。

推送渠道

渠道 条件 实现方式
Browser Push VapidService.public_key 存在 && subscription.browser_push? WebPush.payload_sendVAPID签名)
FCM Push FCM_PROJECT_ID/KEY 配置 && subscription.fcm? Notification::FcmService → FCM.new → push
ChatwootHub ChatwootApp.chatwoot_cloud? ChatwootHub.push_notification_url → HTTP POST

涉及的业务逻辑

  • Notification::PushNotificationService
    • 先检查 user_subscribed_to_notification?push_flags 位检查)
    • 遍历 user.notification_subscriptions,对每个订阅尝试 browser_push → fcm → chatwoot_hub
    • push_message 包含:title, tag(防重复), url(跳转到对话页面)
  • VapidService:管理 VAPID 密钥(从 GlobalConfig/InstallationConfig 读取或自动生成)
  • Notification::FcmServiceGoogle Auth ServiceAccountCredentials → 生成 access_token → FCM client push
  • Notification::PushTestService:推送测试功能,返回 success/failure/skipped 状态用于前端展示

涉及的配置

  • VAPID_KEYSInstallationConfig)— Web Push VAPID 密钥对
  • FCM_PROJECT_ID, FCM_SERVICE_ACCOUNT_KEYEnvironmentConfig)— FCM 配置
  • CHATWOOT_CLOUDInstallationConfig)— 是否启用 ChatwootHub 推送

8. Email通知

功能描述

Email 通知通过 AgentNotifications::ConversationNotificationsMailer 发送,每种通知类型对应一个邮件方法,邮件内容使用 Liquid 模板渲染,支持自定义邮件模板。

邮件类型对应表

通知类型 Mailer方法 邮件主题
conversation_creation conversation_creation(conversation, agent, _user) "{agent_name}, A new conversation [ID-{display_id}] has been created in {inbox_name}"
conversation_assignment conversation_assignment(conversation, agent, _user) "{agent_name}, A new conversation [ID-{display_id}] has been assigned to you."
conversation_mention conversation_mention(conversation, agent, message) "{agent_name}, You have been mentioned in conversation [ID-{display_id}]"
assigned_conversation_new_message assigned_conversation_new_message(conversation, agent, message) "{agent_name}, A new message in conversation [ID-{display_id}]"
participating_conversation_new_message participating_conversation_new_message(conversation, agent, message) 同上
sla_missed_* sla_missed_first_response / next / resolution SLA违规通知

投递条件

  • Notification::EmailNotificationService
    1. notification.read_at 为 nil — 用户尚未通过 Push 已读(避免重复通知)
    2. notification.user.confirmed_at 不为 nil — 邮箱已验证
    3. user_subscribed_to_notification? — email_flags 位检查
    4. notification.account.within_email_rate_limit? — 邮件日限额检查
    5. 通过后 send_notification_emaildeliver_later → 增加邮件计数

Email 限额

  • AccountEmailRateLimitable concern
    • 日限额来源优先级:account.limits.emails → GlobalConfig ACCOUNT_EMAILS_LIMIT → ChatwootApp.max_limit
    • 使用 Redis 计数(key: ACCOUNT_OUTBOUND_EMAIL_COUNT:{account_id}:{date}
    • TTL: 25小时
    • 仅 ChatwootApp.chatwoot_cloud? 时生效

9. 通知Snooze与去重

功能描述

Snooze 功能允许用户将通知暂时延后处理,到期后自动恢复为未读。去重机制确保同一用户对同一 primary_actor(对话)不会产生多条冗余通知。

Snooze

  • APIPOST /api/v1/accounts/{account_id}/notifications/{id}/snooze
  • 设置 snoozed_until 时间戳
  • Notification::ReopenSnoozedNotificationsJob(定时任务):
    • 查询 snoozed_until 在过去3天至当前时间之间的通知
    • 将其 snoozed_until 清空、read_at 清空、meta 记录 last_snoozed_at
    • 更新 last_activity_at 和 updated_at 使通知重新出现在未读列表

去重

  • Notification::RemoveDuplicateNotificationJob
    • 创建通知后异步执行
    • 查找同一 user_id + primary_actor_id 的所有通知
    • 保留最新一条(order(created_at: :desc).first),删除其余
    • 防止同一对话反复触发时产生重复通知

10. WebhookAccount Webhook

功能描述

Webhook 是 Chatwoot 向外部系统推送事件数据的标准机制。账户级 Webhook 由管理员配置 URL + 订阅事件列表,当匹配事件发生时,WebhookListener 构建 payload 并通过 WebhookJob 异步发送 HTTP POST 请求。

用户操作流程

  1. 管理员进入 Settings → Integrations → Webhooks
  2. 点击 "Add Webhook",填写 URL、名称、订阅事件列表
  3. 可选择关联到特定 Inboxinbox_type webhook)或账户全局(account_type webhook
  4. 创建后系统自动生成 secret 用于签名验证
  5. 可通过 "Reset Secret" 重新生成签名密钥

涉及的API端点

方法 路径 说明
GET /api/v1/accounts/{account_id}/webhooks 列出账户所有 Webhook
POST /api/v1/accounts/{account_id}/webhooks 创建 Webhook
PATCH /api/v1/accounts/{account_id}/webhooks/{id} 更新 Webhook
DELETE /api/v1/accounts/{account_id}/webhooks/{id} 删除 Webhook
  • 创建请求:{ webhook: { url, name, inbox_id, subscriptions: [...] } }

涉及的数据模型 + 关键字段

  • Webhookwebhooks 表):
    • id (bigint, PK)
    • url (text) — Webhook 接收端 URL(唯一,同账户下不可重复)
    • name (string) — Webhook 名称
    • secret (string) — HMAC 签名密钥(has_secure_token 自动生成,支持加密存储)
    • subscriptions (jsonb) — 订阅事件列表(数组)
    • webhook_type (integer) — enum: account_type(0), inbox_type(1)
    • account_id (integer, FK)
    • inbox_id (integer, FK, optional) — inbox_type webhook 关联的收件箱
    • created_at, updated_at
  • 唯一索引:index_webhooks_on_account_id_and_url
  • WebhookSecretable concern:提供 has_secure_token :secret + encrypts :secret + reset_secret!

允许的订阅事件(ALLOWED_WEBHOOK_EVENTS

  • conversation_status_changed
  • conversation_updated
  • conversation_created
  • contact_created
  • contact_updated
  • message_created
  • message_updated
  • webwidget_triggered
  • inbox_created
  • inbox_updated
  • conversation_typing_on
  • conversation_typing_off

签名机制

  • 请求头包含:
    • Content-Type: application/json
    • Accept: application/json
    • X-Chatwoot-Delivery: {uuid} — 投递唯一ID
    • X-Chatwoot-Timestamp: {unix_timestamp} — 时间戳
    • X-Chatwoot-Signature: sha256={HMAC-SHA256(secret, timestamp.body)} — 签名

11. IntegrationHook(集成Hook

功能描述

Integrations::Hook 是第三方集成的执行单元,每个 Hook 关联一个 app_id(如 slack, dialogflow, openai, notion, leadsquared, linear 等),绑定到账户级或收件箱级。Hook 接收事件后通过 HookJob 路由到对应集成处理器。

涉及的API端点

方法 路径 说明
POST /api/v1/accounts/{account_id}/integrations/hooks 创建 Hook
PATCH /api/v1/accounts/{account_id}/integrations/hooks/{id} 更新 Hookstatus, settings
POST /api/v1/accounts/{account_id}/integrations/hooks/{id}/process_event 手动触发 Hook 处理事件
DELETE /api/v1/accounts/{account_id}/integrations/hooks/{id} 删除 Hook
  • 创建请求:{ hook: { app_id, inbox_id, status, settings: {...} } }

涉及的数据模型 + 关键字段

  • Integrations::Hookintegrations_hooks 表):
    • id (bigint, PK)
    • app_id (string) — 集成应用标识(slack, dialogflow, openai, notion, leadsquared, linear 等)
    • account_id (integer, FK)
    • inbox_id (integer, FK, optional) — inbox hook 必填,account hook 可为空
    • hook_type (integer) — enum: account(0), inbox(1),根据 app.params[:hook_type] 自动设置
    • status (integer) — enum: disabled(0), enabled(1)
    • access_token (string) — OAuth token(加密存储,可重授权)
    • settings (jsonb) — 集成配置(如 Slack channel_id, Dialogflow project_id 等)
    • reference_id (string) — 外部引用ID
    • created_at, updated_at
  • 验证:
    • account_id 必填
    • app_id 必填
    • inbox_id 在 inbox hook 时必填
    • settings 需通过 app.params[:settings_json_schema] 的 JSON Schema 验证
    • app_id 在同一账户下唯一(除非 app.params[:allow_multiple_hooks]
    • feature_flag 需在账户中启用
    • openai 需验证 API Key 有效性

涉及的业务逻辑

  • HookJobMutexApplicationJob,队列 medium):
    • INTEGRATION_PROCESSORS 路由表:
      • slack → process_slack_integration → SendOnSlackJob / UpdateSlackMessageJob
      • dialogflow → process_dialogflow_integration → DialogflowJob
      • google_translate → google_translate_integration
      • leadsquared → process_leadsquared_integration_with_lock
      • linear → process_linear_integration
    • 跳过 disabled 的 hook
    • 使用 Mutex 防并发

集成列表(从 apps.yml 加载)

  • Slack、Dialogflow、Google Translate、Leadsquared、Linear、Notion、OpenAI(已迁移至 Captain

12. WebhookListener — 事件驱动的Webhook投递

功能描述

WebhookListener 是异步事件监听器,订阅 AsyncDispatcher 中的事件,构建 payload 并投递到账户级 Webhook 和 API Inbox Webhook。

监听事件列表

事件方法 事件名 Payload内容 投递目标
conversation_status_changed conversation.status_changed conversation.webhook_data + event + changed_attributes account webhooks + api inbox webhooks
conversation_updated conversation.updated 同上 同上
conversation_created conversation.created conversation.webhook_data + event 同上
message_created message.created message.webhook_data + event(需 webhook_sendable? 同上
message_updated message.updated 同上 同上
webwidget_triggered webwidget.triggered contact_inbox.webhook_data + event + event_info 同上
contact_created contact.created contact.webhook_data + event 仅 account webhooks
contact_updated contact.updated 同上 + changed_attributes 同上
inbox_created inbox.created inbox push_data + event 仅 account webhooks
inbox_updated inbox.updated 同上 + changed_attributes 同上
conversation_typing_on/off conversation.typing_* event + user + conversation + is_private inbox webhooks + api inbox

投递逻辑

  • deliver_account_webhooks:遍历 account.webhooks.account_type,过滤匹配 subscriptions 的 webhook,调用 WebhookJob
  • deliver_api_inbox_webhooks:仅 Channel::Api 类型且 webhook_url 不空的 inbox,调用 WebhookJob
  • deliver_webhook_payloads:同时投递上述两种
  • WebhookJob 参数:url, payload, webhook_type(:account_webhook/:api_inbox_webhook/:agent_bot_webhook), secret, delivery_id

13. HookListener — 集成Hook执行

功能描述

HookListener 监听事件并执行 IntegrationHooks(集成 Hook)。不同于 WebhookListener 直接 HTTP POSTHookListener 路由到各集成的专有处理器(如 Slack 发消息、Dialogflow AI回复等)。

监听事件列表

事件方法 事件名 执行逻辑
message_created message.created 对所有 account.hooks 执行(过滤 inbox 匹配 + 事件支持) → HookJob
message_updated message.updated 同上
contact_created contact.created 仅执行 account_hooks(无 inbox 限制)
contact_updated contact.updated 同上
conversation_created conversation.created 仅执行 account_hooks
conversation_resolved conversation.resolved 仅在 status == resolved 时执行 account_hooks

执行逻辑

  • execute_hooks:遍历 message.account.hooks,过滤 inbox 不匹配的和事件不支持的,调用 HookJob.perform_later
  • execute_account_hooks:遍历 account.hooks.account_hooks,过滤事件不支持,调用 HookJob.perform_later
  • supported_hook_event?:检查 hook.app.params[:hook_events] 是否包含当前事件名

14. InstallationWebhook(平台级事件推送)

功能描述

InstallationWebhookListener 是平台级(跨账户)事件监听器,仅在 InstallationConfig 中配置了 INSTALLATION_EVENTS_WEBHOOK_URL 时生效。用于 SaaS/Cloud 部署场景下将平台级事件(如账户创建)推送到外部系统。

监听事件

  • account_created:当新账户创建时,推送 payload = account.webhook_data + event + users(管理员列表)
  • 投递:WebhookJob.perform_later(webhook_url, payload)

配置

  • INSTALLATION_EVENTS_WEBHOOK_URLInstallationConfig)— 接收端 URL
  • 仅在 URL 存在时投递

15. 事件分发体系(Dispatcher架构)

功能描述

Chatwoot 采用 Wisper 发布/订阅模式实现事件分发。Dispatcher 是全局单例,在应用初始化时加载所有 Listener。事件分为同步和异步两种分发路径:

架构层次

Dispatcher (Singleton)
  ├── SyncDispatcher (同步)
  │   └── listeners: [ActionCableListener, AgentBotListener]
  │   └── dispatch → Events::Base → publish → 同步回调
  └── AsyncDispatcher (异步)
  │   └── listeners: [AutomationRuleListener, CampaignListener, CsatSurveyListener,
  │       HookListener, InstallationWebhookListener, NotificationListener,
  │       ParticipationListener, UnreadCounts::Listener, ReportingEventListener,
  │       WebhookListener]
  │   └── dispatch → EventDispatcherJob(critical队列) → publish_event → 异步回调

事件触发方式

  • 业务代码中调用 Dispatcher.dispatch(event_name, timestamp, data)
  • 例:Rails.configuration.dispatcher.dispatch(CONVERSATION_CREATED, Time.zone.now, conversation: conversation)
  • Dispatcher.dispatch 同时触发 Sync 和 Async 两条路径

Events::Base

  • 属性:name, timestamp, data
  • method_name:将事件名 conversation.created 转为 conversation_created 方法名
  • Listener 中对应方法接收 Events::Base 对象

事件类型定义(Events::Types

  • 安装级事件:ACCOUNT_CREATED, ACCOUNT_CACHE_INVALIDATED
  • 对话事件:CONVERSATION_CREATED/UPDATED/DELETED/READ/BOT_HANDOFF/OPENED/RESOLVED/STATUS_CHANGED/CONTACT_CHANGED/TYPING_ON/OFF/MENTIONED
  • 分配事件:ASSIGNEE_CHANGED, TEAM_CHANGED
  • 消息事件:MESSAGE_CREATED, FIRST_REPLY_CREATED, REPLY_CREATED, MESSAGE_UPDATED
  • 联系人事件:CONTACT_CREATED/UPDATED/DELETED/MERGED
  • 通知事件:NOTIFICATION_CREATED/UPDATED/DELETED
  • Inbox事件:INBOX_CREATED/UPDATED
  • WebWidget事件:WEBWIDGET_TRIGGERED

跨功能依赖

本模块功能 依赖的其他模块
通知创建 M3 Conversationprimary_actor)、M1 Useruser/secondary_actor
ActionCable 广播 M2 Inboxinbox.members 确定广播目标)、M3 Conversationpush_event_data
Webhook 投递 M3 Conversation/Messagewebhook_data)、M4 Contactwebhook_data
Hook 执行 M2 Inboxhook.inbox)、M6 AutomationHookJob 路由)
Email 限额 M1 Accountlimits.emails)、AccountEmailRateLimitable
Push 订阅 M1 Usernotification_subscriptions
通知权限 M5 Teaminbox membership、conversation access

GoChat 实现建议

功能 建议
Notification 模型 保留 polymorphic actor 设计,但建议将 notification_type 改为 string enumGo 没有 integer enum 习惯)
NotificationSetting 用 JSON/map 存储各通知类型开关,而非位运算(Go 无 FlagShihTzu 对应)
ActionCable 替换为 WebSocket/gorilla 或 nats/gorilla pub-sub,前端用相同事件名协议
Push 通知 浏览器 Push 用 webpush-goFCM 用 firebase-admin-go,可选 ChatwootHub 模式
Email 通知 使用模板引擎(如 mailgun/sendgrid template),邮件限额用 Redis 计数
Webhook 保留签名机制(HMAC-SHA256),HTTP POST 用带超时的 SafeFetch 模式
Hook 路由 用接口/策略模式替代 HookJob 的 INTEGRATION_PROCESSORS 硬编码路由
Dispatcher 替换 Wisper 为 Go channel/event-bus,同步/异步用 goroutine+worker pool
Snooze 保留定时任务恢复机制,可用 cron 或 tick scheduler
去重 保留 RemoveDuplicate 模式,创建后异步清理