chore: Fix conversation status in webhooks (#3364)
- fix the wrong conversation status being sent in webhooks - additional information in websocket events - refactor activity messaging code - move activity message generation to background job to stop the callback loop
This commit is contained in:
102
app/models/concerns/activity_message_handler.rb
Normal file
102
app/models/concerns/activity_message_handler.rb
Normal file
@@ -0,0 +1,102 @@
|
||||
module ActivityMessageHandler
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
private
|
||||
|
||||
def create_activity
|
||||
user_name = Current.user.name if Current.user.present?
|
||||
status_change_activity(user_name) if saved_change_to_status?
|
||||
create_label_change(user_name) if saved_change_to_label_list?
|
||||
end
|
||||
|
||||
def status_change_activity(user_name)
|
||||
create_status_change_message(user_name)
|
||||
queue_conversation_auto_resolution_job if open?
|
||||
end
|
||||
|
||||
def activity_message_params(content)
|
||||
{ account_id: account_id, inbox_id: inbox_id, message_type: :activity, content: content }
|
||||
end
|
||||
|
||||
def create_status_change_message(user_name)
|
||||
content = if user_name
|
||||
I18n.t("conversations.activity.status.#{status}", user_name: user_name)
|
||||
elsif resolved?
|
||||
I18n.t('conversations.activity.status.auto_resolved', duration: auto_resolve_duration)
|
||||
end
|
||||
|
||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||
end
|
||||
|
||||
def create_label_added(user_name, labels = [])
|
||||
return unless labels.size.positive?
|
||||
|
||||
params = { user_name: user_name, labels: labels.join(', ') }
|
||||
content = I18n.t('conversations.activity.labels.added', **params)
|
||||
|
||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||
end
|
||||
|
||||
def create_label_removed(user_name, labels = [])
|
||||
return unless labels.size.positive?
|
||||
|
||||
params = { user_name: user_name, labels: labels.join(', ') }
|
||||
content = I18n.t('conversations.activity.labels.removed', **params)
|
||||
|
||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||
end
|
||||
|
||||
def create_muted_message
|
||||
return unless Current.user
|
||||
|
||||
params = { user_name: Current.user.name }
|
||||
content = I18n.t('conversations.activity.muted', **params)
|
||||
|
||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||
end
|
||||
|
||||
def create_unmuted_message
|
||||
return unless Current.user
|
||||
|
||||
params = { user_name: Current.user.name }
|
||||
content = I18n.t('conversations.activity.unmuted', **params)
|
||||
|
||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||
end
|
||||
|
||||
def generate_team_change_activity_key
|
||||
key = team_id ? 'assigned' : 'removed'
|
||||
key += '_with_assignee' if key == 'assigned' && saved_change_to_assignee_id? && assignee
|
||||
key
|
||||
end
|
||||
|
||||
def generate_team_name_for_activity
|
||||
previous_team_id = previous_changes[:team_id][0]
|
||||
Team.find_by(id: previous_team_id)&.name if previous_team_id.present?
|
||||
end
|
||||
|
||||
def create_team_change_activity(user_name)
|
||||
return unless user_name
|
||||
|
||||
key = generate_team_change_activity_key
|
||||
params = { assignee_name: assignee&.name, team_name: team&.name, user_name: user_name }
|
||||
params[:team_name] = generate_team_name_for_activity if key == 'removed'
|
||||
content = I18n.t("conversations.activity.team.#{key}", **params)
|
||||
|
||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||
end
|
||||
|
||||
def generate_assignee_change_activity_content(user_name)
|
||||
params = { assignee_name: assignee&.name, user_name: user_name }.compact
|
||||
key = assignee_id ? 'assigned' : 'removed'
|
||||
key = 'self_assigned' if self_assign? assignee_id
|
||||
I18n.t("conversations.activity.assignee.#{key}", **params)
|
||||
end
|
||||
|
||||
def create_assignee_change_activity(user_name)
|
||||
return unless user_name
|
||||
|
||||
content = generate_assignee_change_activity_content(user_name)
|
||||
Conversations::ActivityMessageJob.perform_later(self, activity_message_params(content)) if content
|
||||
end
|
||||
end
|
||||
@@ -43,35 +43,4 @@ module AssignmentHandler
|
||||
create_assignee_change_activity(user_name)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_team_change_activity_key
|
||||
key = team_id ? 'assigned' : 'removed'
|
||||
key += '_with_assignee' if key == 'assigned' && saved_change_to_assignee_id? && assignee
|
||||
key
|
||||
end
|
||||
|
||||
def create_team_change_activity(user_name)
|
||||
return unless user_name
|
||||
|
||||
key = generate_team_change_activity_key
|
||||
params = { assignee_name: assignee&.name, team_name: team&.name, user_name: user_name }
|
||||
if key == 'removed'
|
||||
previous_team_id = previous_changes[:team_id][0]
|
||||
params[:team_name] = Team.find_by(id: previous_team_id)&.name if previous_team_id.present?
|
||||
end
|
||||
content = I18n.t("conversations.activity.team.#{key}", **params)
|
||||
|
||||
messages.create(activity_message_params(content))
|
||||
end
|
||||
|
||||
def create_assignee_change_activity(user_name)
|
||||
return unless user_name
|
||||
|
||||
params = { assignee_name: assignee&.name, user_name: user_name }.compact
|
||||
key = assignee_id ? 'assigned' : 'removed'
|
||||
key = 'self_assigned' if self_assign? assignee_id
|
||||
content = I18n.t("conversations.activity.assignee.#{key}", **params)
|
||||
|
||||
messages.create(activity_message_params(content))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -45,6 +45,7 @@ class Conversation < ApplicationRecord
|
||||
include Labelable
|
||||
include AssignmentHandler
|
||||
include RoundRobinHandler
|
||||
include ActivityMessageHandler
|
||||
|
||||
validates :account_id, presence: true
|
||||
validates :inbox_id, presence: true
|
||||
@@ -72,9 +73,7 @@ class Conversation < ApplicationRecord
|
||||
before_save :ensure_snooze_until_reset
|
||||
before_create :mark_conversation_pending_if_bot
|
||||
|
||||
# wanted to change this to after_update commit. But it ended up creating a loop
|
||||
# reinvestigate in future and identity the implications
|
||||
after_update :notify_status_change, :create_activity
|
||||
after_update_commit :execute_after_update_commit_callbacks
|
||||
after_create_commit :notify_conversation_creation, :queue_conversation_auto_resolution_job
|
||||
after_commit :set_display_id, unless: :display_id?
|
||||
|
||||
@@ -150,6 +149,11 @@ class Conversation < ApplicationRecord
|
||||
|
||||
private
|
||||
|
||||
def execute_after_update_commit_callbacks
|
||||
notify_status_change
|
||||
create_activity
|
||||
end
|
||||
|
||||
def ensure_snooze_until_reset
|
||||
self.snoozed_until = nil unless snoozed?
|
||||
end
|
||||
@@ -168,6 +172,8 @@ class Conversation < ApplicationRecord
|
||||
end
|
||||
|
||||
def queue_conversation_auto_resolution_job
|
||||
# FIXME: Move this to one cronjob that iterates over accounts and enqueue resolution jobs
|
||||
# Similar to how we handle campaigns
|
||||
return unless auto_resolve_duration
|
||||
|
||||
AutoResolveConversationsJob.set(wait_until: (last_activity_at || created_at) + auto_resolve_duration.days).perform_later(id)
|
||||
@@ -181,21 +187,6 @@ class Conversation < ApplicationRecord
|
||||
reload
|
||||
end
|
||||
|
||||
def create_activity
|
||||
user_name = Current.user.name if Current.user.present?
|
||||
status_change_activity(user_name) if saved_change_to_status?
|
||||
create_label_change(user_name) if saved_change_to_label_list?
|
||||
end
|
||||
|
||||
def status_change_activity(user_name)
|
||||
create_status_change_message(user_name)
|
||||
queue_conversation_auto_resolution_job if open?
|
||||
end
|
||||
|
||||
def activity_message_params(content)
|
||||
{ account_id: account_id, inbox_id: inbox_id, message_type: :activity, content: content }
|
||||
end
|
||||
|
||||
def notify_status_change
|
||||
{
|
||||
CONVERSATION_OPENED => -> { saved_change_to_status? && open? },
|
||||
@@ -218,16 +209,6 @@ class Conversation < ApplicationRecord
|
||||
return true if previous_changes.key?(:id) || saved_change_to_status?
|
||||
end
|
||||
|
||||
def create_status_change_message(user_name)
|
||||
content = if user_name
|
||||
I18n.t("conversations.activity.status.#{status}", user_name: user_name)
|
||||
elsif resolved?
|
||||
I18n.t('conversations.activity.status.auto_resolved', duration: auto_resolve_duration)
|
||||
end
|
||||
|
||||
messages.create(activity_message_params(content)) if content
|
||||
end
|
||||
|
||||
def create_label_change(user_name)
|
||||
return unless user_name
|
||||
|
||||
@@ -238,42 +219,6 @@ class Conversation < ApplicationRecord
|
||||
create_label_removed(user_name, previous_labels - current_labels)
|
||||
end
|
||||
|
||||
def create_label_added(user_name, labels = [])
|
||||
return unless labels.size.positive?
|
||||
|
||||
params = { user_name: user_name, labels: labels.join(', ') }
|
||||
content = I18n.t('conversations.activity.labels.added', **params)
|
||||
|
||||
messages.create(activity_message_params(content))
|
||||
end
|
||||
|
||||
def create_label_removed(user_name, labels = [])
|
||||
return unless labels.size.positive?
|
||||
|
||||
params = { user_name: user_name, labels: labels.join(', ') }
|
||||
content = I18n.t('conversations.activity.labels.removed', **params)
|
||||
|
||||
messages.create(activity_message_params(content))
|
||||
end
|
||||
|
||||
def create_muted_message
|
||||
return unless Current.user
|
||||
|
||||
params = { user_name: Current.user.name }
|
||||
content = I18n.t('conversations.activity.muted', **params)
|
||||
|
||||
messages.create(activity_message_params(content))
|
||||
end
|
||||
|
||||
def create_unmuted_message
|
||||
return unless Current.user
|
||||
|
||||
params = { user_name: Current.user.name }
|
||||
content = I18n.t('conversations.activity.unmuted', **params)
|
||||
|
||||
messages.create(activity_message_params(content))
|
||||
end
|
||||
|
||||
def mute_key
|
||||
format(Redis::RedisKeys::CONVERSATION_MUTE_KEY, id: id)
|
||||
end
|
||||
|
||||
@@ -97,7 +97,8 @@ class Message < ApplicationRecord
|
||||
data = attributes.merge(
|
||||
created_at: created_at.to_i,
|
||||
message_type: message_type_before_type_cast,
|
||||
conversation_id: conversation.display_id
|
||||
conversation_id: conversation.display_id,
|
||||
conversation: { assignee_id: conversation.assignee_id }
|
||||
)
|
||||
data.merge!(echo_id: echo_id) if echo_id.present?
|
||||
data.merge!(attachments: attachments.map(&:push_event_data)) if attachments.present?
|
||||
|
||||
Reference in New Issue
Block a user