Restructure omni services and add Chatwoot research snapshot
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
class Contacts::BulkActionService
|
||||
def initialize(account:, user:, params:)
|
||||
@account = account
|
||||
@user = user
|
||||
@params = params.deep_symbolize_keys
|
||||
end
|
||||
|
||||
def perform
|
||||
return delete_contacts if delete_requested?
|
||||
return assign_labels if labels_to_add.any?
|
||||
|
||||
Rails.logger.warn("Unknown contact bulk operation payload: #{@params.keys}")
|
||||
{ success: false, error: 'unknown_operation' }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assign_labels
|
||||
Contacts::BulkAssignLabelsService.new(
|
||||
account: @account,
|
||||
contact_ids: ids,
|
||||
labels: labels_to_add
|
||||
).perform
|
||||
end
|
||||
|
||||
def delete_contacts
|
||||
Contacts::BulkDeleteService.new(
|
||||
account: @account,
|
||||
contact_ids: ids
|
||||
).perform
|
||||
end
|
||||
|
||||
def ids
|
||||
Array(@params[:ids]).compact
|
||||
end
|
||||
|
||||
def labels_to_add
|
||||
@labels_to_add ||= Array(@params.dig(:labels, :add)).reject(&:blank?)
|
||||
end
|
||||
|
||||
def delete_requested?
|
||||
@params[:action_name] == 'delete'
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
class Contacts::BulkAssignLabelsService
|
||||
def initialize(account:, contact_ids:, labels:)
|
||||
@account = account
|
||||
@contact_ids = Array(contact_ids)
|
||||
@labels = Array(labels).compact_blank
|
||||
end
|
||||
|
||||
def perform
|
||||
return { success: true, updated_contact_ids: [] } if @contact_ids.blank? || @labels.blank?
|
||||
|
||||
contacts = @account.contacts.where(id: @contact_ids)
|
||||
|
||||
contacts.find_each do |contact|
|
||||
contact.add_labels(@labels)
|
||||
end
|
||||
|
||||
{ success: true, updated_contact_ids: contacts.pluck(:id) }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
class Contacts::BulkDeleteService
|
||||
def initialize(account:, contact_ids: [])
|
||||
@account = account
|
||||
@contact_ids = Array(contact_ids).compact
|
||||
end
|
||||
|
||||
def perform
|
||||
return if @contact_ids.blank?
|
||||
|
||||
contacts.find_each(&:destroy!)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def contacts
|
||||
@account.contacts.where(id: @contact_ids)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,75 @@
|
||||
class Contacts::ContactableInboxesService
|
||||
pattr_initialize [:contact!]
|
||||
|
||||
def get
|
||||
account = contact.account
|
||||
account.inboxes.filter_map { |inbox| get_contactable_inbox(inbox) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_contactable_inbox(inbox)
|
||||
case inbox.channel_type
|
||||
when 'Channel::TwilioSms'
|
||||
twilio_contactable_inbox(inbox)
|
||||
when 'Channel::Whatsapp'
|
||||
whatsapp_contactable_inbox(inbox)
|
||||
when 'Channel::Sms'
|
||||
sms_contactable_inbox(inbox)
|
||||
when 'Channel::Email'
|
||||
email_contactable_inbox(inbox)
|
||||
when 'Channel::Api'
|
||||
api_contactable_inbox(inbox)
|
||||
when 'Channel::WebWidget'
|
||||
website_contactable_inbox(inbox)
|
||||
end
|
||||
end
|
||||
|
||||
def website_contactable_inbox(inbox)
|
||||
latest_contact_inbox = inbox.contact_inboxes.where(contact: @contact).last
|
||||
return unless latest_contact_inbox
|
||||
# FIXME : change this when multiple conversations comes in
|
||||
return if latest_contact_inbox.conversations.present?
|
||||
|
||||
{ source_id: latest_contact_inbox.source_id, inbox: inbox }
|
||||
end
|
||||
|
||||
def api_contactable_inbox(inbox)
|
||||
latest_contact_inbox = inbox.contact_inboxes.where(contact: @contact).last
|
||||
source_id = latest_contact_inbox&.source_id || SecureRandom.uuid
|
||||
|
||||
{ source_id: source_id, inbox: inbox }
|
||||
end
|
||||
|
||||
def email_contactable_inbox(inbox)
|
||||
return if @contact.email.blank?
|
||||
|
||||
{ source_id: @contact.email, inbox: inbox }
|
||||
end
|
||||
|
||||
def whatsapp_contactable_inbox(inbox)
|
||||
return if @contact.phone_number.blank?
|
||||
|
||||
# Remove the plus since thats the format 360 dialog uses
|
||||
{ source_id: @contact.phone_number.delete('+'), inbox: inbox }
|
||||
end
|
||||
|
||||
def sms_contactable_inbox(inbox)
|
||||
return if @contact.phone_number.blank?
|
||||
|
||||
{ source_id: @contact.phone_number, inbox: inbox }
|
||||
end
|
||||
|
||||
def twilio_contactable_inbox(inbox)
|
||||
return if @contact.phone_number.blank?
|
||||
|
||||
case inbox.channel.medium
|
||||
when 'sms'
|
||||
{ source_id: @contact.phone_number, inbox: inbox }
|
||||
when 'whatsapp'
|
||||
{ source_id: "whatsapp:#{@contact.phone_number}", inbox: inbox }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Contacts::ContactableInboxesService.prepend_mod_with('Contacts::ContactableInboxesService')
|
||||
50
research/chatwoot/app/services/contacts/filter_service.rb
Normal file
50
research/chatwoot/app/services/contacts/filter_service.rb
Normal file
@@ -0,0 +1,50 @@
|
||||
class Contacts::FilterService < FilterService
|
||||
ATTRIBUTE_MODEL = 'contact_attribute'.freeze
|
||||
|
||||
def initialize(account, user, params)
|
||||
@account = account
|
||||
# TODO: Change the order of arguments in FilterService maybe?
|
||||
# account, user, params makes more sense
|
||||
super(params, user)
|
||||
end
|
||||
|
||||
def perform
|
||||
validate_query_operator
|
||||
@contacts = query_builder(@filters['contacts'])
|
||||
|
||||
{
|
||||
contacts: @contacts,
|
||||
count: @contacts.count
|
||||
}
|
||||
end
|
||||
|
||||
def filter_values(query_hash)
|
||||
current_val = query_hash['values'][0]
|
||||
if query_hash['attribute_key'] == 'phone_number'
|
||||
"+#{current_val&.delete('+')}"
|
||||
elsif query_hash['attribute_key'] == 'country_code'
|
||||
current_val.downcase
|
||||
else
|
||||
current_val.is_a?(String) ? current_val.downcase : current_val
|
||||
end
|
||||
end
|
||||
|
||||
def base_relation
|
||||
@account.contacts.resolved_contacts(use_crm_v2: @account.feature_enabled?('crm_v2'))
|
||||
end
|
||||
|
||||
def filter_config
|
||||
{
|
||||
entity: 'Contact',
|
||||
table_name: 'contacts'
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def equals_to_filter_string(filter_operator, current_index)
|
||||
return "= :value_#{current_index}" if filter_operator == 'equal_to'
|
||||
|
||||
"!= :value_#{current_index}"
|
||||
end
|
||||
end
|
||||
37
research/chatwoot/app/services/contacts/sync_attributes.rb
Normal file
37
research/chatwoot/app/services/contacts/sync_attributes.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
class Contacts::SyncAttributes
|
||||
attr_reader :contact
|
||||
|
||||
def initialize(contact)
|
||||
@contact = contact
|
||||
end
|
||||
|
||||
def perform
|
||||
update_contact_location_and_country_code
|
||||
set_contact_type
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_contact_location_and_country_code
|
||||
# Ensure that location and country_code are updated from additional_attributes.
|
||||
# TODO: Remove this once all contacts are updated and both the location and country_code fields are standardized throughout the app.
|
||||
@contact.location = @contact.additional_attributes['city']
|
||||
@contact.country_code = @contact.additional_attributes['country']
|
||||
end
|
||||
|
||||
def set_contact_type
|
||||
# If the contact is already a lead or customer then do not change the contact type
|
||||
return unless @contact.contact_type == 'visitor'
|
||||
# If the contact has an email or phone number or social details( facebook_user_id, instagram_user_id, etc) then it is a lead
|
||||
# If contact is from external channel like facebook, instagram, whatsapp, etc then it is a lead
|
||||
return unless @contact.email.present? || @contact.phone_number.present? || social_details_present?
|
||||
|
||||
@contact.contact_type = 'lead'
|
||||
end
|
||||
|
||||
def social_details_present?
|
||||
@contact.additional_attributes.keys.any? do |key|
|
||||
key.start_with?('social_') && @contact.additional_attributes[key].present?
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user