SMS-сообщения могут достигать более 4,5 миллиардов текстовых устройств, чтобы уведомлять людей о предстоящих встречах, чрезвычайных ситуациях, сбоях на дорогах или коммерческих акциях. Программная отправка SMS-сообщений позволяет вам общаться со многими людьми практически одновременно. С помощью Twilio Programmable SMS вы можете создать приложение Node.js, которое отправляет множество сообщений и отчетов, когда каждое из них было доставлено - или нет.

Программируемый SMS Twilio включает вспомогательную библиотеку для Node.js, которая упрощает взаимодействие с API SMS без необходимости создавать и управлять подключениями к конечным точкам API. Как только запрос SMS-сообщения создается с помощью вспомогательной библиотеки, его статус выдается как обещание JavaScript. Хотя это отлично подходит для создания запроса в асинхронном режиме, Promises разрешается только один раз, и запрос сообщения будет проходить через несколько состояний, когда он попадает - или нет - к получателю.

Вы можете создать механизм для мониторинга и отчета о состоянии запроса сообщения с помощью ReactiveX для JavaScript (RxJS) Observables, который может выдавать новые результаты асинхронно по мере изменения данных из базового источника. Создавая оболочку Observable для каждого запроса сообщения и периодически вызывая метод статуса вспомогательной библиотеки, вы можете получать информацию о доставке сообщений для каждого номера и асинхронно сообщать о статусе каждого номера по мере его изменения.

Важное примечание о соответствии. Существуют правила использования SMS-сообщений, которые различаются в зависимости от страны. Ознакомьтесь с правилами стран, в которых вы будете отправлять сообщения, с помощью нормативных требований Twilio для SMS.

Понимание учебного проекта

Из этого туториала Вы узнаете, как создать приложение Node.js, которое отправляет SMS-сообщение на список телефонных номеров и сообщает о статусе каждого сообщения. Вы будете использовать Программируемое SMS-сообщение Twilio для отправки сообщений с помощью Вспомогательной библиотеки Twilio для Node.js в своей программе. Вы создадите оболочку RxJS, механизм, который расширяет существующий код новыми функциями, вокруг функциональности SMS во вспомогательной библиотеке.

Вы узнаете, как создать Observable с нуля и как передать статус SMS-запроса с помощью Observable. Вы узнаете, как использовать операторы RxJS карта, отдельный, объединить, тап и catchError. Вы также создадите механизм тайм-аута, используя функцию таймера RxJS, чтобы отказаться от опроса статуса по истечении интервала тайм-аута.

Предпосылки

Для выполнения задачи в этом посте вам понадобится следующее:

  • Node.js и npm (установка Node.js также установит npm.)
  • Twilio account (Зарегистрируйтесь бесплатно, перейдя по этой ссылке, и получите кредит на счет в размере 10 долларов США.)
  • Twilio CLI
  • Git

Вы также должны иметь практические знания основных элементов JavaScript, асинхронной механики JavaScript и программирования ReactiveX.

На GitHub есть вспомогательный репозиторий для этого поста.

Получение учетных данных вашей учетной записи Twilio

Чтобы использовать интерфейс командной строки Twilio и взаимодействовать с API Twilio, вам потребуются два важных элемента информации из панели управления консоли Twilio: SID учетной записи и токен аутентификации. Вы можете найти их в верхней правой части приборной панели.

Это секреты пользователя, поэтому обязательно храните их в надежном месте.

Чтобы использовать свои учетные данные при разработке, вы захотите сохранить их как переменные среды или в файле .env. Если вы решили сохранить их в файле, убедитесь, что имя файла включено в ваш файл .gitignore, чтобы случайно не зарегистрировать их в общедоступном репозитории. В рамках этого руководства вы будете хранить свои секреты как переменные среды.

Если вы используете операционную систему на основе Unix, например Linux или macOS, вы можете установить переменные среды с помощью следующих команд:

export TWILIO_ACCOUNT_SID=<your account sid>
export TWILIO_AUTH_TOKEN=<your authentication token>

Если вы пользователь Windows, используйте следующие команды:

setx TWILIO_ACCOUNT_SID <your account sid>
setx TWILIO_AUTH_TOKEN <your authentication token>

Получение телефонного номера Twilio

Сообщения Программируемые SMS Twilio отправляются с использованием телефонных номеров Twilio, которые обеспечивают мгновенный доступ к местным, национальным, мобильным и бесплатным телефонным номерам в более чем 100 странах с помощью удобного для разработчиков API. Вы можете получить номер телефона Twilio бесплатно в рамках своей пробной учетной записи.

После создания учетной записи Twilio вы можете получить номер телефона Twilio с помощью Twilio CLI.

Примечание. На момент публикации этого сообщения интерфейс командной строки Twilio находится в стадии бета-тестирования. Если вы устанавливали его ранее, убедитесь, что у вас установлена ​​последняя версия, выполнив следующую команду:

npm update -g twilio-cli

Если вы сохранили свои учетные данные Twilio как переменные среды, интерфейс командной строки Twilio будет использовать их автоматически. Если вы сохранили их каким-либо другим способом, вам необходимо сначала войти в систему, используя следующую команду:

twilio login

Чтобы вывести список доступных для регистрации номеров телефонов, используйте следующую команду, при необходимости заменив США соответствующим кодом страны ISO 3166 alpha-2:

twilio api:core:available-phone-numbers:local:list --country-code US

Вы должны увидеть список, подобный следующему:

Phone Number  Region  ISO Country  Address Requirements
+13852101305  UT      US           none                
+14077922414  FL      US           none                
+16033712156  NH      US           none                
+16036367116  NH      US           none                
+18312751816  CA      US           none                
+14693316717  TX      US           none                
+18312751822  CA      US           none

Скопируйте один из номеров из списка и зарегистрируйте его в своей учетной записи Twilio, используя следующую команду:

twilio api:core:incoming-phone-numbers:create --phone-number="+13852101305"

Если ваша попытка регистрации успешна, вы должны увидеть следующее:

SID                                 Phone Number  Friendly Name 
PN3ef900000000000000000000000000d9  +13852101305  (385) 210-1305

После регистрации номер телефона доступен для использования (до тех пор, пока вы не отпустите его с помощью интерфейса командной строки или консоли Twilio). Обратите внимание, что идентификатор безопасности, связанный с номером телефона, является секретом пользователя и с ним следует безопасно обращаться. Сохраните зарегистрированный номер телефона в переменной окружения TWILIO_PHONE_NUMBER в формате E.164.

Если вы используете Linux, Unix или macOS:

export TWILIO_PHONE_NUMBER=+1234567890

Если вы пользователь Windows:

setx TWILIO_PHONE_NUMBER +1234567890

Вы можете убедиться, что номер был успешно добавлен в вашу учетную запись, отправив тестовое SMS-сообщение на номер телефона с поддержкой SMS:

twilio api:core:messages:create --from $TWILIO_PHONE_NUMBER --to "SMS receiver phone number" --body "Hello world"

Обратите внимание, что с пробной учетной записью вы можете отправлять сообщения только на номера телефонов, которые вы ранее зарегистрировали в своей учетной записи. Телефонный номер с поддержкой SMS, который вы использовали для регистрации своей учетной записи Twilio, - это ваш первый зарегистрированный номер.

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

SID                                 From          To            Status  Direction     Date Sent
SM4a447328e80a43ceb8e61dda9f3d4cb6  +13852101305  +16463974810  queued  outbound-api  null

Через короткое время вы должны получить на свой телефон SMS-сообщение:

Вы можете проверить статус запроса на создание сообщения, используя следующую команду CLI:

twilio api:core:messages:fetch --sid SM4a447328e80a43ceb8e61dda9f3d4cb6

В результате вы увидите:

SID                                 From          To            Status     Direction     Date Sent                    
SM4a447328e80a43ceb8e61dda9f3d4cb6  +13852101305  +16463974810  delivered  outbound-api  Mar 13 2020 14:17:20 GMT+0100

Инициализация проекта Node.js

После регистрации и проверки номера телефона вы можете инициализировать проект и его репозиторий Git с помощью ряда инструкций командной строки.

Откройте окно консоли и выполните следующие инструкции в каталоге, в котором вы хотите создать каталог проекта:

mkdir twilio-sms-rxjs
cd twilio-sms-rxjs
git init
npx license mit > LICENSE
npx gitignore node
npm init -y
git add -A
git commit -m "Initial commit"

Вы можете узнать больше об инициализации проектов Node.js в этом посте Фила Нэша из Twilio.

Установите зависимости, которые вы собираетесь использовать:

npm install esm rxjs twilio

Отправка вашего первого сообщения с помощью Twilio SMS и Node.js

Создайте файл sms-basics.js в корневом каталоге проекта и поместите в него следующий код JavaScript:

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

Программа запускается с инициализации клиента Twilio с использованием SID вашей учетной записи Twilio и токена аутентификации, загруженного из переменных среды. После инициализации client вы можете отправить сообщение с помощью программируемого SMS API Twilio. Для этого вы вызвали client.messages.create метод и передали ему объект, содержащий тело сообщения body, получателя to и отправителя from.

Метод client.messages.create вспомогательной библиотеки Twilio возвращает объект Promise, который разрешается с результатами запроса на создание SMS, отправленного в Twilio Programmable SMS API, показанные на диаграмме зеленым цветом. Вы отправили его в консоль с первым console.log оператором:

{ accountSid: 'xxxxxx',
  apiVersion: '2010-04-01',
  body: 'Hello World!',
  dateCreated: 2020-02-27T21:22:57.000Z,
  dateUpdated: 2020-02-27T21:22:57.000Z,
  dateSent: null,
  direction: 'outbound-api',
  errorCode: null,
  errorMessage: null,
  from: '+xxxxxxxxx',
  messagingServiceSid: null,
  numMedia: '0',
  numSegments: '1',
  price: null,
  priceUnit: 'USD',
  sid: 'SM926d88f994c946f3872011524e20272b',
  status: 'queued',
  subresourceUris:
   { media:
      '/2010-04-01/Accounts/ACe52e8ee9a0993322ce10707c978721fa/Messages/SM926d88f994c946f3872011524e20272b/Media.json' },
  to: '+16463974810',
  uri:
   '/2010-04-01/Accounts/ACe52e8ee9a0993322ce10707c978721fa/Messages/SM926d88f994c946f3872011524e20272b.json' }

Из приведенных выше данных вы выбрали значение sid, которое является уникальным идентификатором вашего запроса, и передали его следующей ссылке в цепочке обещаний. В связанной функции вы вызвали client.messages.fetchmethod, который возвращает другое обещание. Это обещание разрешается с текущим статусом запроса на создание SMS, показанным на диаграмме синим цветом. В последнем then вы выводили этот результат на консоль.

Убедитесь, что вы установили переменные среды TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN и TWILIO_PHONE_NUMBER, и запустите программу:

node -r esm sms-basics.js

Вывод вашей консоли должен быть похож на:

{ accountSid: 'ACe52e8ee9a0993322ce10707c978721fa',
  apiVersion: '2010-04-01',
  body: 'Hello World!',
  dateCreated: 2020-03-22T12:54:49.000Z,
  dateUpdated: 2020-03-22T12:54:49.000Z,
  dateSent: null,
  direction: 'outbound-api',
  errorCode: null,
  errorMessage: null,
  from: '+14252797117',
  messagingServiceSid: null,
  numMedia: '0',
  numSegments: '1',
  price: null,
  priceUnit: 'USD',
  sid: 'SM01a388f69dda4b0da4cffb24fb17b290',
  status: 'queued',
  subresourceUris:
   { media:
      '/2010-04-01/Accounts/ACe52e8ee9a0993322ce10707c978721fa/Messages/SM01a388f69dda4b0da4cffb24fb17b290/Media.json' },
  to: '+16463974810',
  uri:
   '/2010-04-01/Accounts/ACe52e8ee9a0993322ce10707c978721fa/Messages/SM01a388f69dda4b0da4cffb24fb17b290.json' }
{ accountSid: 'ACe52e8ee9a0993322ce10707c978721fa',
  apiVersion: '2010-04-01',
  body: 'Hello World!',
  dateCreated: 2020-03-22T12:54:49.000Z,
  dateUpdated: 2020-03-22T12:54:49.000Z,
  dateSent: 2020-03-22T12:54:49.000Z,
  direction: 'outbound-api',
  errorCode: null,
  errorMessage: null,
  from: '+14252797117',
  messagingServiceSid: null,
  numMedia: '0',
  numSegments: '1',
  price: '-0.00750',
  priceUnit: 'USD',
  sid: 'SM01a388f69dda4b0da4cffb24fb17b290',
  status: 'sent',
  subresourceUris:
   { media:
      '/2010-04-01/Accounts/ACe52e8ee9a0993322ce10707c978721fa/Messages/SM01a388f69dda4b0da4cffb24fb17b290/Media.json',
     feedback:
      '/2010-04-01/Accounts/ACe52e8ee9a0993322ce10707c978721fa/Messages/SM01a388f69dda4b0da4cffb24fb17b290/Feedback.json' },
  to: '+16463974810',
  uri:
   '/2010-04-01/Accounts/ACe52e8ee9a0993322ce10707c978721fa/Messages/SM01a388f69dda4b0da4cffb24fb17b290.json' }

Если вы не выполняли кодирование и хотите наверстать упущенное на этом этапе, используя код из репозитория GitHub, выполните следующие команды в каталоге, в котором вы хотите создать каталог проекта:

git clone https://github.com/maciejtreder/twilio-sms-rxjs.git
cd twilio-sms-rxjs
git checkout step1
npm install

Отправка SMS-сообщений на несколько телефонных номеров и мониторинг состояния сообщения с помощью оболочки RxJS Observables

Теперь, когда у вас есть основы Twilio SMS, вы можете создать оболочку RxJS для Twilio SMS API, которая является частью библиотеки twilio.

Создайте новый файл с именем rxjs-twilio.js в корневом каталоге проекта. Начните кодирование с введения операторов импорта и инициализации клиента Twilio, вставив следующий код:

Определите функцию sendSMS, которая является точкой входа в файл rxjs-twilio. Вставьте следующий код JavaScript в конец файла rxjs-twilio.js:

Функция sendSMS принимает два параметра: номер телефона получателя и текст сообщения. Константа subject представляет Observable, возвращаемый функцией. Subject - одна из реализаций интерфейса Observable.

Функция client.messages.create создает запрос на создание SMS-сообщения через Twilio API. Когда обещание, возвращаемое функцией client.messages.create, разрешается, статус запроса на создание передается через subject Observable.

Метод pollStatus, который вы реализуете дальше, отслеживает состояние Observable, возвращаемого вспомогательной библиотекой Twilio. Если запрос на создание сообщения, отправленный в Twilio SMS API, завершается неудачно, ошибка передается через subject Observable внутри блока catch.

Функция sendSMS использует оператор distinct, чтобы не выдавать повторяющееся значение статуса. Если статус сообщения не изменился от одного вызова pollStatus к другому.

Вставьте следующий код в конец файла rxjs-twilio.js:

Константа messageDeliveryStatuses - это массив строк, представляющих значения Завершенного состояния доставки сообщения для сообщения.

Массив stopPolling будет содержать словарь флагов, указывающих, истек ли тайм-аут опроса для каждого ожидающего запроса SMS-сообщения. Это необходимо, поскольку статус «отправлено» может указывать либо на то, что сообщение находится в процессе доставки, либо на то, что оно было доставлено, и Twilio не получил подтверждения доставки или другого статуса от оператора связи.

Функция pollStatus принимает до четырех параметров, последние два необязательны:

  • sid - идентификатор запроса на создание сообщения для опроса
  • subject - статус запроса на создание сообщения
  • timeout - время в секундах, по истечении которого опрос должен прекратиться
  • watchdog - подписка на Observable, созданную с помощью метода timer, который определяет, когда перевернуть флаг в массиве stopPolling на true для связанного идентификатора сообщения

Если статус вашего запроса на создание сообщения изменяется на одно из значений messageDeliveryStatuses или для флага stopPolling[sid] установлено значение true, метод unsubscribe прекращает работу сторожевого таймера и очищает очередь обратного вызова, а метод splice удаляет запись из словаря stopPolling, чтобы избежать создания утечка памяти.

Если статус запроса не является одним из окончательных значений статуса доставки, функция pollStatus вызывается рекурсивно. Чтобы избежать установки нового сторожевого таймера для каждого рекурсивного выполнения функции во внутренних pollStatus вызовах, существующий watchdog передается в качестве аргумента в предложении else.

Метод client.messages(sid).fetch() возвращает обещание, которое разрешается с помощью объекта, представляющего состояние каждого SMS-запроса. Когда Promise разрешается, результат передается через subject Observable, который был передан как параметр из функции sendSMS.

Поток обертки показан на диаграмме ниже:

Тестирование оболочки RxJS Observables

Чтобы использовать оболочку Observables и посмотреть, как она обрабатывает непредсказуемую последовательность ответов от SMS API, вам понадобится программа, которая перебирает серию телефонных номеров и собирает Observables из каждого номера в коллекцию. Затем вы можете сообщать о статусе каждого SMS-сообщения каждый раз, когда Observable в коллекции излучает новую информацию, указывающую на изменение статуса связанного запроса сообщения.

Создайте файл с именем sms.js в корне проекта и вставьте следующий код JavaScript:

Код создает массив телефонных номеров для отправки SMS-сообщения. Он выполняет итерацию по каждому элементу массива, вызывает функцию sendSMS и передает сообщение «Hello World!» сообщение на него вместе с номером телефона.

В целях тестирования используйте номера телефонов, которые могут принимать SMS-сообщения, например ваш зарегистрированный номер. Также указывайте номера, например стационарные, которые являются действительными номерами, но не могут принимать SMS-сообщения. Включите значения, которые не являются допустимыми номерами телефонов, чтобы проверить, как программа и API обрабатывают недопустимые данные.

Observable, возвращаемый sendSMS, передается по конвейеру следующим операторам:

  • map удаляет некоторую информацию из ответа API Twilio, оставляя только номер телефона и текущий статус.
  • catchError выявляет ошибки, которые в конечном итоге возникают, и возвращает Observable, излучающий идентификатор запроса, который не удался, и причину сбоя.
  • map добавляет метку времени к ответу.

Каждый Observable, возвращаемый sendSMS, помещается в массив requests, который затем передается в качестве параметра методу merge для объединения массива в один Observable.

Консолидированный Observable передается по конвейеру через функцию tap, которая выполняет побочное действие каждый раз, когда Observable генерирует новый результат, создавая строку в выходных данных между каждым набором результатов при обновлении одного из запросов SMS-сообщения.

Действие, выполняемое оператором subscribe, отправляет результаты, выданные Observable, на консоль.

Запускаем программу:

node -r esm sms.js

Результат должен быть похож на:

{ number: 'non-existing',
  status: 'error',
  details:
   { Error: The 'To' number  is not a valid phone number.
     status: 400,
     message: 'The \'To\' number  is not a valid phone number.',
     code: 21211,
     moreInfo: 'https://www.twilio.com/docs/errors/21211',
     detail: undefined },
  time: 2020-04-02T18:52:55.532Z }
-------------------------------
{ number: '+484110677',
  status: 'error',
  details:
   { Error: The 'To' number +484110677 is not a valid phone number.
     status: 400,
     message: 'The \'To\' number +484110677 is not a valid phone number.',
     code: 21211,
     moreInfo: 'https://www.twilio.com/docs/errors/21211',
     detail: undefined },
  time: 2020-04-02T18:52:55.538Z }
-------------------------------
{ number: '+15017122661',
  status: 'queued',
  time: 2020-04-02T18:52:55.593Z }
-------------------------------
{ number: '+15017122661',
  status: 'sent',
  time: 2020-04-02T18:52:56.194Z }
-------------------------------
{ number: '+15017122661',
  status: 'delivered',
  time: 2020-04-02T18:52:56.818Z }

В выходных данных отображается состояние трех запросов на создание сообщений к программируемому API SMS Twilio через вспомогательную библиотеку для Node.js. Каждый раз, когда статус любого из запросов изменяется, создается новый вывод.

Два телефонных номера, использованные в примере кода, не могут принимать SMS, поэтому API будет возвращать сообщения об ошибках. Ошибки такого типа обычно возвращаются сразу.

Для телефонных номеров, способных принимать SMS, Observable обычно отправляет данные три раза, потому что запрос сообщения будет иметь три состояния: поставлено в очередь, отправлено и доставлено. Исключение составляют случаи, когда сообщение не может быть доставлено, например, когда телефон выключен или оператор не возвращает ответ Twilio. В таких случаях конечное состояние будет «отправлено».

Если вы не выполняли кодирование и хотите наверстать упущенное на этом этапе, используя код из репозитория GitHub, выполните следующие команды в каталоге, в котором вы хотите создать каталог проекта:

git clone https://github.com/maciejtreder/twilio-sms-rxjs.git
cd twilio-sms-rxjs
git checkout step2
npm install

Резюме

В этом посте вы познакомились с использованием программируемых SMS-сообщений Twilio с наблюдаемыми объектами RxJS в приложении Node.js. Вы узнали, как выполнять повторяющиеся вызовы API и отправлять ответы с помощью Observables. Вы видели, как объединить три Observable, чтобы создать новый, который выдает данные всякий раз, когда один из внутренних Observable излучает данные. Используя эти методы, вы можете получать обновления о статусе доставки SMS-сообщений, отправленных с помощью Программируемых SMS-сообщений Twilio.

Дополнительные ресурсы

Программируемые SMS Twilio - отправляйте и получайте текстовые сообщения по всему миру с помощью API, от которого зависят более миллиона разработчиков.

Введение в ReactiveX и учебные пособия - на веб-сайте ReactiveX есть обширный список ресурсов для получения дополнительных сведений о реактивном программировании и различных языковых реализациях. Это сторонние продукты, поэтому смотрите и читайте на свое усмотрение.

RxJS - сайт RxJS - это место для канонической информации о библиотеке ReactiveX для JavaScript.

Какой оператор я использую? - Полезный инструмент для выбора лучшего оператора Observables для желаемого действия.

Если вы хотите узнать больше о обещаниях JavaScript, которые являются лучшим асинхронным инструментом для некоторых ситуаций программирования, ознакомьтесь со следующими сообщениями:

Если вы хотите узнать больше о RxJS Observables, прочтите этот пост:

Готовы к еще большему обмену сообщениями? Попробуйте несколько программируемых SMS-миссий в TwilioQuest!

Я Мачей Тредер, свяжитесь со мной через [email protected], https://www.maciejtreder.com или @maciejtreder на GitHub, Twitter и LinkedIn.

Этот пост изначально опубликован в блоге Twilio.