mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-29 02:02:27 +00:00
106 lines
3.1 KiB
Ruby
106 lines
3.1 KiB
Ruby
# This Builder will create a contact inbox with specified attributes. If the contact inbox already exists, it will be returned.
|
||
# For Specific Channels like whatsapp, email etc . it smartly generated appropriate the source id when none is provided.
|
||
|
||
class ContactInboxBuilder
|
||
pattr_initialize [:contact, :inbox, :source_id, { hmac_verified: false }]
|
||
|
||
def perform
|
||
@source_id ||= generate_source_id
|
||
create_contact_inbox if source_id.present?
|
||
end
|
||
|
||
private
|
||
|
||
def generate_source_id
|
||
case @inbox.channel_type
|
||
when 'Channel::TwilioSms'
|
||
twilio_source_id
|
||
when 'Channel::Whatsapp'
|
||
wa_source_id
|
||
when 'Channel::Email'
|
||
email_source_id
|
||
when 'Channel::Sms'
|
||
phone_source_id
|
||
when 'Channel::Api', 'Channel::WebWidget'
|
||
SecureRandom.uuid
|
||
else
|
||
raise "Unsupported operation for this channel: #{@inbox.channel_type}"
|
||
end
|
||
end
|
||
|
||
def email_source_id
|
||
raise ActionController::ParameterMissing, 'contact email' unless @contact.email
|
||
|
||
@contact.email
|
||
end
|
||
|
||
def phone_source_id
|
||
raise ActionController::ParameterMissing, 'contact phone number' unless @contact.phone_number
|
||
|
||
@contact.phone_number
|
||
end
|
||
|
||
def wa_source_id
|
||
raise ActionController::ParameterMissing, 'contact phone number' unless @contact.phone_number
|
||
|
||
# whatsapp doesn't want the + in e164 format
|
||
@contact.phone_number.delete('+').to_s
|
||
end
|
||
|
||
def twilio_source_id
|
||
raise ActionController::ParameterMissing, 'contact phone number' unless @contact.phone_number
|
||
|
||
case @inbox.channel.medium
|
||
when 'sms'
|
||
@contact.phone_number
|
||
when 'whatsapp'
|
||
"whatsapp:#{@contact.phone_number}"
|
||
end
|
||
end
|
||
|
||
def create_contact_inbox
|
||
attrs = {
|
||
contact_id: @contact.id,
|
||
inbox_id: @inbox.id,
|
||
source_id: @source_id
|
||
}
|
||
|
||
::ContactInbox.where(attrs).first_or_create!(hmac_verified: hmac_verified || false)
|
||
rescue ActiveRecord::RecordNotUnique
|
||
Rails.logger.info("[ContactInboxBuilder] RecordNotUnique #{@source_id} #{@contact.id} #{@inbox.id}")
|
||
update_old_contact_inbox
|
||
retry
|
||
end
|
||
|
||
def update_old_contact_inbox
|
||
# The race condition occurs when there’s a contact inbox with the
|
||
# same source ID but linked to a different contact. This can happen
|
||
# if the agent updates the contact’s email or phone number, or
|
||
# if the contact is merged with another.
|
||
#
|
||
# We update the old contact inbox source_id to a random value to
|
||
# avoid disrupting the current flow. However, the root cause of
|
||
# this issue is a flaw in the contact inbox model design.
|
||
# Contact inbox is essentially tracking a session and is not
|
||
# needed for non-live chat channels.
|
||
raise ActiveRecord::RecordNotUnique unless allowed_channels?
|
||
|
||
contact_inbox = ::ContactInbox.find_by(inbox_id: @inbox.id, source_id: @source_id)
|
||
return if contact_inbox.blank?
|
||
|
||
contact_inbox.update!(source_id: new_source_id)
|
||
end
|
||
|
||
def new_source_id
|
||
if @inbox.whatsapp? || @inbox.sms? || @inbox.twilio?
|
||
"whatsapp:#{@source_id}#{rand(100)}"
|
||
else
|
||
"#{rand(10)}#{@source_id}"
|
||
end
|
||
end
|
||
|
||
def allowed_channels?
|
||
@inbox.email? || @inbox.sms? || @inbox.twilio? || @inbox.whatsapp?
|
||
end
|
||
end
|