Restructure omni services and add Chatwoot research snapshot
This commit is contained in:
139
research/chatwoot/app/actions/contact_identify_action.rb
Normal file
139
research/chatwoot/app/actions/contact_identify_action.rb
Normal file
@@ -0,0 +1,139 @@
|
||||
# retain_original_contact_name: false / true
|
||||
# In case of setUser we want to update the name of the identified contact,
|
||||
# which is the default behaviour
|
||||
#
|
||||
# But, In case of contact merge during prechat form contact update.
|
||||
# We don't want to update the name of the identified original contact.
|
||||
|
||||
class ContactIdentifyAction
|
||||
include UrlHelper
|
||||
pattr_initialize [:contact!, :params!, { retain_original_contact_name: false, discard_invalid_attrs: false }]
|
||||
|
||||
def perform
|
||||
@attributes_to_update = [:identifier, :name, :email, :phone_number]
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
merge_if_existing_identified_contact
|
||||
merge_if_existing_email_contact
|
||||
merge_if_existing_phone_number_contact
|
||||
update_contact
|
||||
end
|
||||
@contact
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def account
|
||||
@account ||= @contact.account
|
||||
end
|
||||
|
||||
def merge_if_existing_identified_contact
|
||||
return unless merge_contacts?(existing_identified_contact, :identifier)
|
||||
|
||||
process_contact_merge(existing_identified_contact)
|
||||
end
|
||||
|
||||
def merge_if_existing_email_contact
|
||||
return unless merge_contacts?(existing_email_contact, :email)
|
||||
|
||||
process_contact_merge(existing_email_contact)
|
||||
end
|
||||
|
||||
def merge_if_existing_phone_number_contact
|
||||
return unless merge_contacts?(existing_phone_number_contact, :phone_number)
|
||||
return unless mergable_phone_contact?
|
||||
|
||||
process_contact_merge(existing_phone_number_contact)
|
||||
end
|
||||
|
||||
def process_contact_merge(mergee_contact)
|
||||
@contact = merge_contact(mergee_contact, @contact)
|
||||
@attributes_to_update.delete(:name) if retain_original_contact_name
|
||||
end
|
||||
|
||||
def existing_identified_contact
|
||||
return if params[:identifier].blank?
|
||||
|
||||
@existing_identified_contact ||= account.contacts.find_by(identifier: params[:identifier])
|
||||
end
|
||||
|
||||
def existing_email_contact
|
||||
return if params[:email].blank?
|
||||
|
||||
@existing_email_contact ||= account.contacts.from_email(params[:email])
|
||||
end
|
||||
|
||||
def existing_phone_number_contact
|
||||
return if params[:phone_number].blank?
|
||||
|
||||
@existing_phone_number_contact ||= account.contacts.find_by(phone_number: params[:phone_number])
|
||||
end
|
||||
|
||||
def merge_contacts?(existing_contact, key)
|
||||
return if existing_contact.blank?
|
||||
|
||||
return true if params[:identifier].blank?
|
||||
|
||||
# we want to prevent merging contacts with different identifiers
|
||||
if existing_contact.identifier.present? && existing_contact.identifier != params[:identifier]
|
||||
# we will remove attribute from update list
|
||||
@attributes_to_update.delete(key)
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
# case: contact 1: email: 1@test.com, phone: 123456789
|
||||
# params: email: 2@test.com, phone: 123456789
|
||||
# we don't want to overwrite 1@test.com since email parameter takes higer priority
|
||||
def mergable_phone_contact?
|
||||
return true if params[:email].blank?
|
||||
|
||||
if existing_phone_number_contact.email.present? && existing_phone_number_contact.email != params[:email]
|
||||
@attributes_to_update.delete(:phone_number)
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def update_contact
|
||||
@contact.attributes = params.slice(*@attributes_to_update).reject do |_k, v|
|
||||
v.blank?
|
||||
end.merge({ custom_attributes: custom_attributes, additional_attributes: additional_attributes })
|
||||
# blank identifier or email will throw unique index error
|
||||
# TODO: replace reject { |_k, v| v.blank? } with compact_blank when rails is upgraded
|
||||
@contact.discard_invalid_attrs if discard_invalid_attrs
|
||||
@contact.save!
|
||||
enqueue_avatar_job
|
||||
end
|
||||
|
||||
def enqueue_avatar_job
|
||||
return unless params[:avatar_url].present? && !@contact.avatar.attached?
|
||||
return unless url_valid?(params[:avatar_url])
|
||||
|
||||
Avatar::AvatarFromUrlJob.perform_later(@contact, params[:avatar_url])
|
||||
end
|
||||
|
||||
def merge_contact(base_contact, merge_contact)
|
||||
return base_contact if base_contact.id == merge_contact.id
|
||||
|
||||
ContactMergeAction.new(
|
||||
account: account,
|
||||
base_contact: base_contact,
|
||||
mergee_contact: merge_contact
|
||||
).perform
|
||||
end
|
||||
|
||||
def custom_attributes
|
||||
return @contact.custom_attributes if params[:custom_attributes].blank?
|
||||
|
||||
(@contact.custom_attributes || {}).deep_merge(params[:custom_attributes].stringify_keys)
|
||||
end
|
||||
|
||||
def additional_attributes
|
||||
return @contact.additional_attributes if params[:additional_attributes].blank?
|
||||
|
||||
(@contact.additional_attributes || {}).deep_merge(params[:additional_attributes].stringify_keys)
|
||||
end
|
||||
end
|
||||
62
research/chatwoot/app/actions/contact_merge_action.rb
Normal file
62
research/chatwoot/app/actions/contact_merge_action.rb
Normal file
@@ -0,0 +1,62 @@
|
||||
class ContactMergeAction
|
||||
include Events::Types
|
||||
pattr_initialize [:account!, :base_contact!, :mergee_contact!]
|
||||
|
||||
def perform
|
||||
# This case happens when an agent updates a contact email in dashboard,
|
||||
# while the contact also update his email via email collect box
|
||||
return @base_contact if base_contact.id == mergee_contact.id
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
validate_contacts
|
||||
merge_conversations
|
||||
merge_messages
|
||||
merge_contact_inboxes
|
||||
merge_contact_notes
|
||||
merge_and_remove_mergee_contact
|
||||
end
|
||||
@base_contact
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_contacts
|
||||
return if belongs_to_account?(@base_contact) && belongs_to_account?(@mergee_contact)
|
||||
|
||||
raise StandardError, 'contact does not belong to the account'
|
||||
end
|
||||
|
||||
def belongs_to_account?(contact)
|
||||
@account.id == contact.account_id
|
||||
end
|
||||
|
||||
def merge_conversations
|
||||
Conversation.where(contact_id: @mergee_contact.id).update(contact_id: @base_contact.id)
|
||||
end
|
||||
|
||||
def merge_contact_notes
|
||||
Note.where(contact_id: @mergee_contact.id, account_id: @mergee_contact.account_id).update(contact_id: @base_contact.id)
|
||||
end
|
||||
|
||||
def merge_messages
|
||||
Message.where(sender: @mergee_contact).update(sender: @base_contact)
|
||||
end
|
||||
|
||||
def merge_contact_inboxes
|
||||
ContactInbox.where(contact_id: @mergee_contact.id).update(contact_id: @base_contact.id)
|
||||
end
|
||||
|
||||
def merge_and_remove_mergee_contact
|
||||
mergable_attribute_keys = %w[identifier name email phone_number additional_attributes custom_attributes]
|
||||
base_contact_attributes = base_contact.attributes.slice(*mergable_attribute_keys).compact_blank
|
||||
mergee_contact_attributes = mergee_contact.attributes.slice(*mergable_attribute_keys).compact_blank
|
||||
|
||||
# attributes in base contact are given preference
|
||||
merged_attributes = mergee_contact_attributes.deep_merge(base_contact_attributes)
|
||||
|
||||
@mergee_contact.reload.destroy!
|
||||
Rails.configuration.dispatcher.dispatch(CONTACT_MERGED, Time.zone.now, contact: @base_contact,
|
||||
tokens: [@base_contact.contact_inboxes.filter_map(&:pubsub_token)])
|
||||
@base_contact.update!(merged_attributes)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user