fix(slack): Sync bot interactive responses (#14076)
When a customer responds to a bot's interactive prompt (input_select, input_csat, form, input_email) from the widget, the response shows up in the Chatwoot agent UI but is not reflected in the linked Slack channel — Slack only ever shows the original question. This happens because the widget submits the answer as an UPDATE to the original message (writing `content_attributes.submitted_values` or `submitted_email`), but the Slack hook only listened to `message.created`, so updates were ignored. Closes https://linear.app/chatwoot/issue/PLA-147 ### Preview <img width="1290" height="1106" alt="CleanShot 2026-04-21 at 13 19 19@2x" src="https://github.com/user-attachments/assets/cd2a9d3f-89d3-4e81-9230-5b078e1b7b44" /> ### How to test 1. Connect a web widget inbox to a Slack channel. 2. Trigger each bot message type (input_select, form, input_csat, input_email) in a conversation. 3. Submit responses from the widget. 4. Verify each response now appears in the Slack thread, appended to the original bot question. --------- Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Muhsin
Claude Opus 4.6
Copilot
parent
7820739f3a
commit
f7bbd40816
+17
-6
@@ -23,13 +23,24 @@ class HookJob < MutexApplicationJob
|
||||
private
|
||||
|
||||
def process_slack_integration(hook, event_name, event_data)
|
||||
return unless ['message.created'].include?(event_name)
|
||||
|
||||
message = event_data[:message]
|
||||
if message.attachments.blank?
|
||||
::SendOnSlackJob.perform_later(message, hook)
|
||||
else
|
||||
::SendOnSlackJob.set(wait: 2.seconds).perform_later(message, hook)
|
||||
|
||||
case event_name
|
||||
when 'message.created'
|
||||
if message.attachments.blank?
|
||||
::SendOnSlackJob.perform_later(message, hook)
|
||||
else
|
||||
::SendOnSlackJob.set(wait: 2.seconds).perform_later(message, hook)
|
||||
end
|
||||
when 'message.updated'
|
||||
# Only interactive bot messages store responses via content_attributes (submitted_values / submitted_email).
|
||||
# Skip other content types to avoid unnecessary job enqueues on every message update.
|
||||
return unless message.content_type.in?(Integrations::Slack::UpdateSlackMessageService::SUPPORTED_CONTENT_TYPES)
|
||||
# Guard against redundant Slack updates when unrelated attributes change (e.g. status)
|
||||
# while submitted_values is already present on the message.
|
||||
return unless event_data[:previous_changes]&.key?('content_attributes')
|
||||
|
||||
::UpdateSlackMessageJob.perform_later(message, hook)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
class UpdateSlackMessageJob < MutexApplicationJob
|
||||
queue_as :medium
|
||||
retry_on LockAcquisitionError, wait: 1.second, attempts: 8
|
||||
|
||||
def perform(message, hook)
|
||||
key = format(::Redis::Alfred::SLACK_MESSAGE_MUTEX, conversation_id: message.conversation_id, reference_id: hook.reference_id)
|
||||
with_lock(key) do
|
||||
Integrations::Slack::UpdateSlackMessageService.new(message: message, hook: hook).perform
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -43,7 +43,7 @@ class HookListener < BaseListener
|
||||
next if hook.inbox.present? && hook.inbox != message.inbox
|
||||
next unless supported_hook_event?(hook, event.name)
|
||||
|
||||
HookJob.perform_later(hook, event.name, message: message)
|
||||
HookJob.perform_later(hook, event.name, message: message, previous_changes: event.data[:previous_changes])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -59,7 +59,7 @@ class HookListener < BaseListener
|
||||
return false if hook.disabled?
|
||||
|
||||
supported_events_map = {
|
||||
'slack' => ['message.created'],
|
||||
'slack' => ['message.created', 'message.updated'],
|
||||
'dialogflow' => ['message.created', 'message.updated'],
|
||||
'google_translate' => ['message.created'],
|
||||
'leadsquared' => ['contact.updated', 'conversation.created', 'conversation.resolved']
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
class Integrations::Slack::UpdateSlackMessageService
|
||||
include RegexHelper
|
||||
|
||||
SUPPORTED_CONTENT_TYPES = %w[input_select form input_csat input_email].freeze
|
||||
|
||||
pattr_initialize [:message!, :hook!]
|
||||
|
||||
def perform
|
||||
return unless updateable_message?
|
||||
|
||||
slack_client.chat_update(
|
||||
channel: hook.reference_id,
|
||||
ts: slack_message_ts,
|
||||
text: updated_message_content
|
||||
)
|
||||
rescue Slack::Web::Api::Errors::MessageNotFound => e
|
||||
# Original Slack message no longer exists (e.g. channel was reconfigured), skip gracefully.
|
||||
Rails.logger.error "[Slack] chat_update failed (account=#{message.account_id}, hook=#{hook.id}): #{e.message}"
|
||||
rescue Slack::Web::Api::Errors::IsArchived, Slack::Web::Api::Errors::AccountInactive, Slack::Web::Api::Errors::MissingScope,
|
||||
Slack::Web::Api::Errors::InvalidAuth,
|
||||
Slack::Web::Api::Errors::ChannelNotFound, Slack::Web::Api::Errors::NotInChannel => e
|
||||
Rails.logger.error "[Slack] chat_update failed (account=#{message.account_id}, hook=#{hook.id}): #{e.message}"
|
||||
hook.prompt_reauthorization!
|
||||
hook.disable
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def updateable_message?
|
||||
hook&.reference_id.present? &&
|
||||
slack_message_ts.present? &&
|
||||
message.content_type.in?(SUPPORTED_CONTENT_TYPES) &&
|
||||
(message.submitted_values.present? || message.submitted_email.present?)
|
||||
end
|
||||
|
||||
def slack_message_ts
|
||||
source_id = message.external_source_id_slack.to_s
|
||||
return unless source_id.start_with?('cw-origin-')
|
||||
|
||||
source_id.delete_prefix('cw-origin-').presence
|
||||
end
|
||||
|
||||
def updated_message_content
|
||||
question = sanitized_content(message_text).presence
|
||||
response = formatted_response
|
||||
|
||||
return question.to_s if response.blank?
|
||||
|
||||
[question, response].compact.join("\n\n")
|
||||
end
|
||||
|
||||
def formatted_response
|
||||
case message.content_type
|
||||
when 'input_select'
|
||||
format_input_select_response
|
||||
when 'form'
|
||||
format_form_response
|
||||
when 'input_csat'
|
||||
format_csat_response
|
||||
when 'input_email'
|
||||
format_email_response
|
||||
end
|
||||
end
|
||||
|
||||
def format_input_select_response
|
||||
item = Array(message.submitted_values).first
|
||||
return if item.blank?
|
||||
|
||||
value = item['title'] || item[:title] || item['value'] || item[:value]
|
||||
value = sanitized_content(value)
|
||||
return if value.blank?
|
||||
|
||||
"*Response:* #{value}"
|
||||
end
|
||||
|
||||
def format_email_response
|
||||
email = sanitized_content(message.submitted_email)
|
||||
return if email.blank?
|
||||
|
||||
"*Email:* #{email}"
|
||||
end
|
||||
|
||||
def format_form_response
|
||||
submitted_values = Array(message.submitted_values)
|
||||
return if submitted_values.blank?
|
||||
|
||||
items_by_name = Array(message.items).index_by { |i| flex_value(i, 'name') }
|
||||
|
||||
lines = submitted_values.filter_map do |sv|
|
||||
format_form_line(sv, items_by_name)
|
||||
end
|
||||
|
||||
return if lines.blank?
|
||||
|
||||
"*Responses:*\n#{lines.join("\n")}"
|
||||
end
|
||||
|
||||
def format_csat_response
|
||||
csat_response = flex_value(message.submitted_values, 'csat_survey_response', 'csatSurveyResponse')
|
||||
return if csat_response.blank?
|
||||
|
||||
rating = flex_value(csat_response, 'rating')
|
||||
feedback = flex_value(csat_response, 'feedback_message', 'feedbackMessage')
|
||||
|
||||
lines = []
|
||||
lines << "• Rating: #{rating}" if rating.present?
|
||||
lines << "• Feedback: #{sanitized_content(feedback)}" if feedback.present?
|
||||
|
||||
return if lines.blank?
|
||||
|
||||
"*CSAT:*\n#{lines.join("\n")}"
|
||||
end
|
||||
|
||||
def format_form_line(submitted_value, items_by_name)
|
||||
name = flex_value(submitted_value, 'name')
|
||||
value = sanitized_content(flex_value(submitted_value, 'value'))
|
||||
return if value.blank?
|
||||
|
||||
label = sanitized_content(flex_value(items_by_name[name], 'label') || name)
|
||||
return if label.blank?
|
||||
|
||||
"• #{label}: #{value}"
|
||||
end
|
||||
|
||||
def flex_value(hash, *keys)
|
||||
return if hash.blank?
|
||||
|
||||
keys.each do |key|
|
||||
value = hash[key.to_sym] || hash[key.to_s]
|
||||
return value if value.present?
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def message_text
|
||||
content = message.processed_message_content || message.content
|
||||
|
||||
if content.present?
|
||||
content.to_s.gsub(MENTION_REGEX, '\1')
|
||||
else
|
||||
content
|
||||
end
|
||||
end
|
||||
|
||||
def sanitized_content(text)
|
||||
ActionView::Base.full_sanitizer.sanitize(text.to_s).strip
|
||||
end
|
||||
|
||||
def slack_client
|
||||
@slack_client ||= Slack::Web::Client.new(token: hook.access_token)
|
||||
end
|
||||
end
|
||||
@@ -67,6 +67,44 @@ RSpec.describe HookJob do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when handleable events like message.updated for slack' do
|
||||
let(:process_service) { double }
|
||||
|
||||
before do
|
||||
allow(process_service).to receive(:perform)
|
||||
end
|
||||
|
||||
it 'calls UpdateSlackMessageJob when content_attributes changed' do
|
||||
message = create(:message, account: account, content: 'Pick one', message_type: :outgoing,
|
||||
content_type: :input_select, content_attributes: { items: [{ title: 'A', value: 'a' }] })
|
||||
hook = create(:integrations_hook, app_id: 'slack', account: account)
|
||||
event_data = { message: message, previous_changes: { 'content_attributes' => [{}, { 'submitted_values' => [{ 'title' => 'A' }] }] } }
|
||||
|
||||
allow(UpdateSlackMessageJob).to receive(:perform_later).and_return(process_service)
|
||||
expect(UpdateSlackMessageJob).to receive(:perform_later).with(message, hook)
|
||||
described_class.perform_now(hook, 'message.updated', event_data)
|
||||
end
|
||||
|
||||
it 'does not call UpdateSlackMessageJob when content_attributes did not change' do
|
||||
message = create(:message, account: account, content: 'Pick one', message_type: :outgoing,
|
||||
content_type: :input_select, content_attributes: { items: [{ title: 'A', value: 'a' }] })
|
||||
hook = create(:integrations_hook, app_id: 'slack', account: account)
|
||||
event_data = { message: message, previous_changes: { 'status' => %w[sent delivered] } }
|
||||
|
||||
expect(UpdateSlackMessageJob).not_to receive(:perform_later)
|
||||
described_class.perform_now(hook, 'message.updated', event_data)
|
||||
end
|
||||
|
||||
it 'does not call UpdateSlackMessageJob for unsupported content types' do
|
||||
message = create(:message, account: account, content: 'Hello', message_type: :outgoing, content_type: :text)
|
||||
hook = create(:integrations_hook, app_id: 'slack', account: account)
|
||||
event_data = { message: message, previous_changes: { 'content_attributes' => [{}, {}] } }
|
||||
|
||||
expect(UpdateSlackMessageJob).not_to receive(:perform_later)
|
||||
described_class.perform_now(hook, 'message.updated', event_data)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when processing leadsquared integration' do
|
||||
let(:contact) { create(:contact, account: account) }
|
||||
let(:conversation) { create(:conversation, account: account, contact: contact) }
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe UpdateSlackMessageJob do
|
||||
let(:account) { create(:account) }
|
||||
let(:hook) { create(:integrations_hook, app_id: 'slack', account: account) }
|
||||
let(:message) do
|
||||
create(:message, account: account, content: 'Pick one', message_type: :outgoing, content_type: :input_select,
|
||||
content_attributes: { items: [{ title: 'Option A', value: 'a' }] })
|
||||
end
|
||||
|
||||
before do
|
||||
stub_request(:post, 'https://slack.com/api/chat.update')
|
||||
end
|
||||
|
||||
it 'calls Integrations::Slack::UpdateSlackMessageService' do
|
||||
service_instance = Integrations::Slack::UpdateSlackMessageService.new(message: message, hook: hook)
|
||||
expect(Integrations::Slack::UpdateSlackMessageService).to receive(:new).with(message: message, hook: hook).and_return(service_instance)
|
||||
|
||||
described_class.perform_now(message, hook)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,245 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Integrations::Slack::UpdateSlackMessageService do
|
||||
let(:account) { create(:account) }
|
||||
let(:channel_email) { create(:channel_email, account: account) }
|
||||
let(:contact) { create(:contact, account: account) }
|
||||
let(:conversation) { create(:conversation, inbox: channel_email.inbox, contact: contact, identifier: '12345.6789') }
|
||||
let(:hook) { create(:integrations_hook, account: account, reference_id: 'C123') }
|
||||
let(:slack_client) { double }
|
||||
|
||||
before do
|
||||
allow(Slack::Web::Client).to receive(:new).and_return(slack_client)
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
context 'with input_select' do
|
||||
it 'updates the Slack message with the submitted response' do
|
||||
message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: channel_email.inbox,
|
||||
conversation: conversation,
|
||||
message_type: :outgoing,
|
||||
content_type: :input_select,
|
||||
content: 'Pick one',
|
||||
content_attributes: {
|
||||
items: [{ title: 'Option A', value: 'a' }, { title: 'Option B', value: 'b' }],
|
||||
submitted_values: [{ title: 'Option A', value: 'a' }]
|
||||
},
|
||||
external_source_id_slack: 'cw-origin-6789.12345'
|
||||
)
|
||||
|
||||
expect(slack_client).to receive(:chat_update).with(
|
||||
channel: 'C123',
|
||||
ts: '6789.12345',
|
||||
text: a_string_including('Pick one', 'Option A')
|
||||
)
|
||||
|
||||
described_class.new(message: message, hook: hook).perform
|
||||
end
|
||||
end
|
||||
|
||||
context 'with form' do
|
||||
it 'updates the Slack message with all submitted fields' do
|
||||
message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: channel_email.inbox,
|
||||
conversation: conversation,
|
||||
message_type: :outgoing,
|
||||
content_type: :form,
|
||||
content: 'Please fill this',
|
||||
content_attributes: {
|
||||
items: [{ name: 'email', label: 'Email' }, { name: 'company', label: 'Company' }],
|
||||
submitted_values: [{ name: 'email', value: 'a@example.com' }, { name: 'company', value: 'Acme' }]
|
||||
},
|
||||
external_source_id_slack: 'cw-origin-6789.12345'
|
||||
)
|
||||
|
||||
expect(slack_client).to receive(:chat_update).with(
|
||||
channel: 'C123',
|
||||
ts: '6789.12345',
|
||||
text: a_string_including('Please fill this', 'Email', 'a@example.com', 'Company', 'Acme')
|
||||
)
|
||||
|
||||
described_class.new(message: message, hook: hook).perform
|
||||
end
|
||||
end
|
||||
|
||||
context 'with input_csat' do
|
||||
it 'updates the Slack message with the CSAT rating and feedback' do
|
||||
message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: channel_email.inbox,
|
||||
conversation: conversation,
|
||||
message_type: :outgoing,
|
||||
content_type: :input_csat,
|
||||
content: 'Rate us',
|
||||
content_attributes: {
|
||||
submitted_values: {
|
||||
'csat_survey_response' => { 'rating' => 5, 'feedback_message' => 'Great support!' }
|
||||
}
|
||||
},
|
||||
external_source_id_slack: 'cw-origin-6789.12345'
|
||||
)
|
||||
|
||||
expect(slack_client).to receive(:chat_update).with(
|
||||
channel: 'C123',
|
||||
ts: '6789.12345',
|
||||
text: a_string_including('Rate us', 'Rating: 5', 'Great support!')
|
||||
)
|
||||
|
||||
described_class.new(message: message, hook: hook).perform
|
||||
end
|
||||
end
|
||||
|
||||
context 'with input_email' do
|
||||
it 'updates the Slack message with the submitted email' do
|
||||
message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: channel_email.inbox,
|
||||
conversation: conversation,
|
||||
message_type: :outgoing,
|
||||
content_type: :input_email,
|
||||
content: 'Get notified by email',
|
||||
content_attributes: {
|
||||
submitted_email: 'user@example.com'
|
||||
},
|
||||
external_source_id_slack: 'cw-origin-6789.12345'
|
||||
)
|
||||
|
||||
expect(slack_client).to receive(:chat_update).with(
|
||||
channel: 'C123',
|
||||
ts: '6789.12345',
|
||||
text: a_string_including('Get notified by email', 'user@example.com')
|
||||
)
|
||||
|
||||
described_class.new(message: message, hook: hook).perform
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is no submitted response' do
|
||||
it 'skips the update' do
|
||||
message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: channel_email.inbox,
|
||||
conversation: conversation,
|
||||
message_type: :outgoing,
|
||||
content_type: :input_select,
|
||||
content: 'Pick one',
|
||||
content_attributes: {
|
||||
items: [{ title: 'Option A', value: 'a' }]
|
||||
},
|
||||
external_source_id_slack: 'cw-origin-6789.12345'
|
||||
)
|
||||
|
||||
expect(slack_client).not_to receive(:chat_update)
|
||||
|
||||
described_class.new(message: message, hook: hook).perform
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the message was not originated from Chatwoot' do
|
||||
it 'skips the update' do
|
||||
message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: channel_email.inbox,
|
||||
conversation: conversation,
|
||||
message_type: :outgoing,
|
||||
content_type: :input_select,
|
||||
content: 'Pick one',
|
||||
content_attributes: {
|
||||
items: [{ title: 'Option A', value: 'a' }],
|
||||
submitted_values: [{ title: 'Option A', value: 'a' }]
|
||||
},
|
||||
external_source_id_slack: '6789.12345'
|
||||
)
|
||||
|
||||
expect(slack_client).not_to receive(:chat_update)
|
||||
|
||||
described_class.new(message: message, hook: hook).perform
|
||||
end
|
||||
end
|
||||
|
||||
context 'when hook has no reference_id' do
|
||||
it 'skips the update' do
|
||||
hook_without_ref = create(:integrations_hook, account: account, reference_id: nil)
|
||||
message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: channel_email.inbox,
|
||||
conversation: conversation,
|
||||
message_type: :outgoing,
|
||||
content_type: :input_select,
|
||||
content: 'Pick one',
|
||||
content_attributes: {
|
||||
items: [{ title: 'Option A', value: 'a' }],
|
||||
submitted_values: [{ title: 'Option A', value: 'a' }]
|
||||
},
|
||||
external_source_id_slack: 'cw-origin-6789.12345'
|
||||
)
|
||||
|
||||
expect(slack_client).not_to receive(:chat_update)
|
||||
|
||||
described_class.new(message: message, hook: hook_without_ref).perform
|
||||
end
|
||||
end
|
||||
|
||||
context 'when Slack API raises an auth error' do
|
||||
it 'disables the hook and prompts reauthorization' do
|
||||
message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: channel_email.inbox,
|
||||
conversation: conversation,
|
||||
message_type: :outgoing,
|
||||
content_type: :input_select,
|
||||
content: 'Pick one',
|
||||
content_attributes: {
|
||||
items: [{ title: 'Option A', value: 'a' }],
|
||||
submitted_values: [{ title: 'Option A', value: 'a' }]
|
||||
},
|
||||
external_source_id_slack: 'cw-origin-6789.12345'
|
||||
)
|
||||
|
||||
allow(slack_client).to receive(:chat_update).and_raise(Slack::Web::Api::Errors::AccountInactive.new('account_inactive'))
|
||||
allow(hook).to receive(:prompt_reauthorization!)
|
||||
|
||||
described_class.new(message: message, hook: hook).perform
|
||||
|
||||
expect(hook).to have_received(:prompt_reauthorization!)
|
||||
expect(hook.reload.status).to eq('disabled')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the original Slack message no longer exists' do
|
||||
it 'skips gracefully without disabling the hook' do
|
||||
message = create(
|
||||
:message,
|
||||
account: account,
|
||||
inbox: channel_email.inbox,
|
||||
conversation: conversation,
|
||||
message_type: :outgoing,
|
||||
content_type: :input_select,
|
||||
content: 'Pick one',
|
||||
content_attributes: {
|
||||
items: [{ title: 'Option A', value: 'a' }],
|
||||
submitted_values: [{ title: 'Option A', value: 'a' }]
|
||||
},
|
||||
external_source_id_slack: 'cw-origin-6789.12345'
|
||||
)
|
||||
|
||||
allow(slack_client).to receive(:chat_update).and_raise(Slack::Web::Api::Errors::MessageNotFound.new('message_not_found'))
|
||||
|
||||
described_class.new(message: message, hook: hook).perform
|
||||
|
||||
expect(hook.reload.status).not_to eq('disabled')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -9,7 +9,7 @@ describe HookListener do
|
||||
create(:message, message_type: 'outgoing',
|
||||
account: account, inbox: inbox, conversation: conversation)
|
||||
end
|
||||
let!(:event) { Events::Base.new(event_name, Time.zone.now, message: message) }
|
||||
let!(:event) { Events::Base.new(event_name, Time.zone.now, message: message, previous_changes: nil) }
|
||||
let(:contact_event) { Events::Base.new('contact.updated', Time.zone.now, contact: conversation.contact) }
|
||||
let(:conversation_event) { Events::Base.new('conversation.created', Time.zone.now, conversation: conversation) }
|
||||
|
||||
@@ -26,7 +26,7 @@ describe HookListener do
|
||||
context 'when hook is configured' do
|
||||
it 'triggers hook job' do
|
||||
hook = create(:integrations_hook, account: account)
|
||||
expect(HookJob).to receive(:perform_later).with(hook, 'message.created', message: message).once
|
||||
expect(HookJob).to receive(:perform_later).with(hook, 'message.created', message: message, previous_changes: nil).once
|
||||
listener.message_created(event)
|
||||
end
|
||||
end
|
||||
@@ -45,7 +45,7 @@ describe HookListener do
|
||||
context 'when hook is configured' do
|
||||
it 'triggers hook job' do
|
||||
hook = create(:integrations_hook, :dialogflow, account: account, inbox: inbox)
|
||||
expect(HookJob).to receive(:perform_later).with(hook, 'message.updated', message: message).once
|
||||
expect(HookJob).to receive(:perform_later).with(hook, 'message.updated', message: message, previous_changes: nil).once
|
||||
listener.message_updated(event)
|
||||
end
|
||||
end
|
||||
@@ -66,21 +66,21 @@ describe HookListener do
|
||||
context 'when hook is enabled and app_id is supported' do
|
||||
it 'enqueues the job for slack' do
|
||||
hook = create(:integrations_hook, account: account)
|
||||
expect(HookJob).to receive(:perform_later).with(hook, event_name, message: message)
|
||||
expect(HookJob).to receive(:perform_later).with(hook, event_name, message: message, previous_changes: nil)
|
||||
|
||||
listener.message_created(event)
|
||||
end
|
||||
|
||||
it 'enqueues the job for dialogflow' do
|
||||
hook = create(:integrations_hook, :dialogflow, account: account, inbox: inbox)
|
||||
expect(HookJob).to receive(:perform_later).with(hook, event_name, message: message)
|
||||
expect(HookJob).to receive(:perform_later).with(hook, event_name, message: message, previous_changes: nil)
|
||||
|
||||
listener.message_created(event)
|
||||
end
|
||||
|
||||
it 'enqueues the job for google_translate' do
|
||||
hook = create(:integrations_hook, :google_translate, account: account)
|
||||
expect(HookJob).to receive(:perform_later).with(hook, event_name, message: message)
|
||||
expect(HookJob).to receive(:perform_later).with(hook, event_name, message: message, previous_changes: nil)
|
||||
|
||||
listener.message_created(event)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user