Допустим, у меня есть CSV с телефонными номерами. Я хочу проверить или отфильтровать эти телефонные номера по географическому местоположению, например по городу, штату, региону, оператору связи, часовому поясу и типу телефона (мобильный/стационарный/VoIP/и т. д.). Быстрый поиск в гугле дает несколько ссылок, связанных с SMS: Numverify, Nexmo и т. д., большинство из которых требует подписки или каким-то образом ограничено.

Что привлекло мое внимание, так это предложение Amazon — Pinpoint. Это широкое предложение, которое может помочь с маркетинговыми кампаниями, привлечением пользователей, аналитикой взаимодействия и т. д., которое включает в себя SMS-сервис и проверку номера телефона.

При цене 0,006 доллара за звонок (на момент написания статьи) это стоит недорого. Могут быть и другие бесплатные инструменты, но я скептически отношусь к тому, насколько сомнительными они могут оказаться, кэшируя мою ценную базу данных; Кроме того, если я уже использую другие компоненты AWS для различных нужд, это действительно быстрая интеграция для проверки телефонных номеров с помощью чего-то такого простого, как вызов CLI, с уже настроенными учетными данными/профилем AWS —

aws pinpoint phone-number-validate --number-validate-request IsoCountryCode="+1",PhoneNumber="+14085550100"

Вывод выглядит так-

Этого достаточно для случайной проверки телефонных номеров. Но у меня есть гигантский CSV размером в несколько гигабайт для обработки. Быстрое и грязное решение в RoR, так как у меня уже есть проект rails -

Добавьте необходимый SDK AWS в Gemfile

#Gemfile
gem 'aws-sdk-pinpoint'

Отбросьте этот метод везде, где он подходит, возможно, в /lib под class-

PHONE_COLUMN = 34.freeze

def process_phone_numbers(filename)
  pinpoint = Aws::Pinpoint::Client.new
  CSV.foreach(filename, headers: true) do |row, i|
    query = {
      number_validate_request: {
        iso_country_code: '+1',
        phone_number:     '+1' + row[PHONE_COLUMN]
      }
    }
    response = pinpoint.phone_number_validate(query).number_validate_response
    pp response
  end
end

Это просто красивая печать ответов на экране, не очень полезная. Давайте создадим еще один CSV-файл только с теми строками, которые удовлетворяют определенному условию, скажем, только с мобильными номерами. Измените приведенный выше файл, чтобы условно записать строку в новый CSV.

Во-первых, нам нужен новый файл CSV. Я создаю это, вставляя _verified в уже существующее имя файла.

def process_phone_numbers(filename)
  pinpoint = Aws::Pinpoint::Client.new

  verified_filename = filename.dup
  verified_filename.insert(-5, '_verified')
...

Затем я открываю этот CSV для записи и циклического прохождения по условию.

def process_phone_numbers(filename)
  pinpoint = Aws::Pinpoint::Client.new

  verified_filename = filename.dup
  verified_filename.insert(-5, '_verified')

  CSV.open(verified_filename, 'wb') do |v_csv|
    CSV.foreach(filename, headers: true) do |row, i|
      {
        number_validate_request: {
          iso_country_code: '+1',
          phone_number:     '+1' + row[PHONE_COLUMN]
        }
      }

      response = pinpoint.phone_number_validate().number_validate_response
      next unless response[:phone_type] == "MOBILE"
      v_csv << row
      v_csv.flush
      Rails.logger.info "⛳️ Wrote #{row[PHONE_COLUMN]} to file"
    end
  end
end

Теперь у меня есть файл CSV, содержащий только строки, содержащие номера мобильных телефонов! И да, у меня есть флаг в моем журнале. ⛳️

Что, если бы у меня было несколько строк с одинаковым номером? Что, если бы это была база данных пользователей/людей, в которой несколько человек использовали один и тот же номер? Что делать, если AWS выдает ошибку? (Иногда это происходит, если вы отправляете неверный номер)

Кэшируйте это. Лови.

Для этого я разделяю часть запроса AWS и часть проверки на новые функции.

def valid_mobile?(number)
  r = phone_number_validate number
  r[:phone_type] == 'MOBILE'
end

def pinpoint_client
  @pinpoint_client ||= Aws::Pinpoint::Client.new
end

def phone_number_validate(number)
  Rails.cache.fetch(number, expires_in: 1.year.from_now) do
    query = { number_validate_request: { iso_country_code: '+1', phone_number: number } }
    r     = pinpoint_client.phone_number_validate(query)
    r.number_validate_response.to_h
  end
rescue StandardError => e
  Rails.logger.error e.message
  raise e # Lets stop if there are problems. Anyway there is cache.
end

Первоначальный метод теперь должен выглядеть так, чтобы использовать вышеуказанное:

def process_phone_numbers(filename)

  verified_filename = filename.dup
  verified_filename.insert(-5, '_verified')

  CSV.open(verified_filename, 'wb') do |v_csv|
    CSV.foreach(filename, headers: true) do |row, i|

      next unless valid_mobile? row[PHONE_COLUMN]

      v_csv << row
      v_csv.flush
      Rails.logger.info "⛳️ Wrote #{row[PHONE_COLUMN]} to file"
    end
  end
end

Там. Теперь у нас есть метод phone_number_validate, который можно использовать повторно, он был закеширован. Метод process_phone_numbers может обрабатывать CSV.

Конечно, есть и другие краеугольные случаи, такие как пропуск пустых строк, обработка чисел в формате +1(123)-(456)-7890 и т. д. Есть достаточно вопросов StackOverflow, касающихся этого, например, использование чрезвычайно сложного регулярного выражения.

Это я оставляю читателю в качестве упражнения!