Class: ContactPointLineTypeLookupHandler

Inherits:
ApplicationJob
  • Object
show all
Includes:
RailsEventStore::AsyncHandler
Defined in:
app/subscribers/contact_point_line_type_lookup_handler.rb

Overview

Hits Twilio Lookup v2 line-type intelligence on phone-change events and
updates contact_points.sms_status from the carrier-reported line type.

Logic Details

Why this is decoupled from contact_point.category: the user's category
choice (PHONE/CELL/FAX) is intent, not ground truth. A phone field
filled in via the customer creation flow is just as likely to be a mobile
as a landline, and a CELL category is just a guess until Twilio confirms.
Category stays as user intent; sms_status is the single SMS-routing
source of truth, with sms_status_source recording why it's set the way
it is.

Skips numbers verified within RECENT_VERIFICATION_WINDOW so a flurry of
detail edits on the same record (or post-merge contact-point churn) doesn't
burn lookups. Also skips PBX seats (extension present) — SMS to a
specific extension is never possible.

Constant Summary collapse

RECENT_VERIFICATION_WINDOW =

Window during which a previously-verified record is left alone.

90.days
SMS_CAPABLE_LINE_TYPES =

Twilio v2 line types that map to sms_enabled.

%i[mobile fixed_voip non_fixed_voip personal toll_free uan].freeze
SMS_INCAPABLE_LINE_TYPES =

Twilio v2 line types that map to sms_none.

%i[landline voicemail pager].freeze

Instance Method Summary collapse

Instance Method Details

#perform(event) ⇒ void

This method returns an undefined value.

Looks up the contact point's line type via Twilio and stamps the result.

Parameters:

Raises:

  • (ActiveRecord::RecordInvalid)

    when the contact point fails to
    persist



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'app/subscribers/contact_point_line_type_lookup_handler.rb', line 47

def perform(event)
  cp = ContactPoint.find_by(id: event.data[:contact_point_id])
  return unless cp&.callable? && cp.detail.present?
  return if cp.extension.present? # PBX seat — never SMS-able to the extension itself
  return if recently_verified?(cp)

  line_type = TwilioClient.lookup_line_type(cp.detail)
  new_status = decide_sms_status(line_type, cp.sms_status)
  return if new_status.nil? # :unknown / unmapped — leave status alone, no breadcrumb either

  cp.update!(
    sms_status: new_status,
    sms_status_source: 'twilio_lookup',
    sms_status_verified_at: Time.current
  )
end