Это была интересная задача, с которой я столкнулся недавно, и это был первый раз, когда я почувствовал
впервые за долгое время, когда я почувствовал зуд решить проблему только потому.
потому что я хотел посмотреть, смогу ли я это сделать.
Прошло (проверяю заметки) около 7
лет с тех пор, как я в последний раз писал что-то, связанное с ruby, и, вероятно, 5 лет
с тех пор, как я выступал с докладами или презентациями.
Не думаю, что я осознавал, насколько я выдохся после нескольких бурных лет как в личном, так и в профессиональном плане.
как в личном, так и в профессиональном плане. Было здорово почувствовать, что я должен решить проблему
проблему просто ради удовольствия и даже был вынужден написать об этом.
С учетом всего вышесказанного, я уже зарвался, так что потерпите.
Недавно я работал над сайд-проектом, который использует Twilio для отправки и получения текстовых сообщений.
Я всегда считал SMS действительно мощным инструментом, который можно добавить в арсенал любого приложения, поскольку он использует то, что пользователи уже используют много раз в день.
что пользователи уже используют много раз в день. В недавнем отчете компания Twilio даже
сообщила, что 96% сообщений читаются в течение трех минут после получения, а 90% – в течение трех секунд!
В этой статье мы не будем подробно останавливаться на отправке текстовых сообщений. Если вы хотите получить
больше информации о том, как отправлять текстовые сообщения, то
twilio-ruby gem есть несколько примеров.
Телефонные номера Twilio имеют веб-крючки как для голосовой связи, так и для SMS. Эти веб-крючки
вызываются каждый раз, когда на ваш телефонный номер Twilio поступает входящее текстовое сообщение или телефонный звонок.
Пример входящего сообщения
Оба канала имеют URL-адрес резервного копирования, если первый запрос к веб-крючку не сработал.
Отправка запросов Webhook в ваше локальное окружение
Если вы когда-либо разрабатывали функции сторонних веб-крючков или сервисов с помощью локального окружения, то, скорее всего, это было сделано с помощью эксплоринга.
окружением, вы, вероятно, делали это, открывая свое локальное окружение для доступа в
интернет с помощью чего-то вроде ngrok.
Если вы еще не слышали о ngrok, то это один из моих любимых инструментов, который я
все больше и больше интегрирую в рабочий процесс. Вы можете прочитать больше о ngrok
здесь.
Запуск Ngrok в вашем Procfile
С переходом Rails 7 на использование foreman и Procfiles для запуска и запуска
сервисов, я добавил ngrok как собственный процесс, чтобы он всегда был доступен.
пример Procfile.dev
# Procfile.dev
web: bin/rails server -p $PORT
css: yarn build:css --watch
js: yarn build --reload
worker: bundle exec sidekiq
stripe: stripe listen --forward-to $PORT/webhooks/stripe
ngrok: ngrok http --log=stdout $PORT
Обновление URL-адресов веб-крючков Twilio
Когда ngrok запущен, теперь есть URL, на который можно направить ваши веб-крючки.
Один из вариантов обновления URL веб-крючков – взять URL ngrok, зайти в консоль Twilio и обновить его.
в консоль Twilio и обновить URL webhook для голоса и смс.
Это было хорошо для меня… некоторое время.
Моя основная проблема с этой установкой заключается в том, что каждый раз, когда я перезапускаю свои службы, URL ngrok
меняется и его нужно обновлять снова. Это может привести к большому количеству обновлений при
запуске и остановке служб и ошибок, когда забываешь обновить
URL каждый раз.
Кроме того, если вы делаете это не очень часто, легко
забыть, как найти раздел для обновления URL-адресов веб-крючков.
Именно тогда я почувствовал желание найти способ автоматизировать эту процедуру, когда она мне понадобится.
Покопавшись в документации Twilio, я нашел несколько опций для установки
URL веб-крючков с помощью их гема ruby.
Еще немного покопавшись, я нашел конечную точку для ngrok, которую можно запросить, чтобы получить
его публичный URL.
Теперь мы готовим на газе. Имея URL нашего локального экземпляра ngrok и способ
программно обновлять URL веб-крючков, у нас есть две необходимые части.
Следующий шаг – решить, как выполнять этот код, когда нам нужны обновления.
Для меня это была задача rake. Это дает мне хороший повторяющийся способ обновления
URL, когда это необходимо.
Задача Rake для обновления URL-адресов
Прежде чем я начну рассматривать подробности, вот код задачи rake. На сайте
зависимостями для задачи rake являются запущенный экземпляр ngrok, twilio-ruby
gem (я использую версию 5.67.1),
и, если вы хотите, чтобы в вашем терминале было больше цвета, чтобы было легче заметить, гем colorize.
Вот пример задачи rake
namespace :development do
desc "updates twilio webhook urls to ngrok url"
task set_twilio_webhooks: :environment do
return unless Rails.env.development?
begin
response = JSON.parse(Net::HTTP.get(URI("http://localhost:4040/api/tunnels")))
ngrok_url = response.dig("tunnels", 1, "public_url")
rescue Errno::ECONNREFUSED
puts "*******NGROK not running*******".yellow
end
if ngrok_url
puts ngrok_url.green
account_sid = Rails.application.credentials.dig(:twilio)[:account_sid]
auth_token = Rails.application.credentials.dig(:twilio)[:auth_token]
development_incoming_phone_number = Rails.application.credentials.dig(:twilio)[:auth_token]
twilio_client = Twilio::Client.new(account_sid, auth_token)
twilio_client.api.v2010.accounts(account_sid).incoming_phone_numbers(developemnt_incoming_phone_number)
.update(sms_url: "#{ngrok_url}/webhooks/twilio",
sms_fallback_url: "#{ngrok_url}/webhooks/twilio_fallback",
voice_url: "#{ngrok_url}/webhooks/twilio_voice",
voice_fallback_url: "#{ngrok_url}/webhooks/twilio_voice_fallback")
puts "Webhook URLs updated!".green
rescue Twilio::REST::RestError => e
puts e
end
end
end
end
Подробности
Теперь давайте немного углубимся в суть происходящего.
Внутри задачи rake первое, что я делаю, это убеждаюсь, что мы находимся в разделе
required, но я подумал, что это хорошая проверка, чтобы случайно не обновить
информацию вне нашей среды development
.
После того, как мы убедились, что находимся в нашей среде dev, нам нужно найти URL-адрес
запущенного экземпляра ngrok. Ngrok имеет конечную точку API, работающую на порту 4040
для
текущих туннелей. Эта конечная точка возвращает XML-ответ с информацией о
текущих туннелях. Возвращается 2 туннеля, и я беру тот, у которого
именем command_line (http)
, который является вторым из возвращенных.
Обычно в качестве библиотеки HTTP в ruby используется HTTParty, но я хотел настроить это, используя
библиотеку ruby Net::HTTP, чтобы не вводить еще одну зависимость.
Убедившись, что мы находимся в среде development
, Net::HTTP затем делает
запрос к конечной точке ngrok и анализирует ответ XML.
Вот несколько примеров ответа, который он отправляет обратно.
Усеченный ответ Ngrok
<tunnelListResource>
<Tunnels>
</Tunnels>
<Tunnels>
</Tunnels>
<URI>/api/tunnels</URI>
</tunnelListResource>
Одиночный туннельный объект
<Tunnels>
<Name>command_line</Name>
<URI>/api/tunnels/command_line</URI>
<PublicURL>https://7be0-72-42-102-194.ngrok.io</PublicURL>
<Proto>https</Proto>
<Config>
<Addr>http://localhost:80</Addr>
<Inspect>true</Inspect>
</Config>
<Metrics>
<Conns>
</Conns>
<HTTP>
<Count>0</Count>
<Rate1>0</Rate1>
<Rate5>0</Rate5>
<Rate15>0</Rate15>
<P50>0</P50>
<P90>0</P90>
<P95>0</P95>
<P99>0</P99>
</HTTP>
</Metrics>
</Tunnels>
Задача rake берет разобранный XML и захватывает 2-й возвращенный туннель. Почему вы
спросите вы?
Честно говоря, это, скорее всего, было первое, что сработало из StackOverflow, и я пошел дальше.
дальше.
<PublicURL>
для обоих туннелей, похоже, всегда одинаковы, так что, возможно.
возможно, любой из них будет работать.
Если ngrok не запущен, мы получим ошибку Errno::ECONNREFUSED
, поэтому мы можем
спасти эту ошибку, и выведем некоторый вывод желтым цветом, чтобы было легко понять, когда
что-то пошло не так
Захватите публичный урл Ngrok
begin
response = JSON.parse(Net::HTTP.get(URI("http://localhost:4040/api/tunnels")))
ngrok_url = response.dig("tunnels", 1, "public_url")
rescue Errno::ECONNREFUSED
puts "*******NGROK not running*******".yellow
end
Теперь у нас есть повторяемый и надежный способ получения URL-адреса ngrok в любой момент времени.
работает. Мы также получаем цветной вывод, чтобы дать нам быстрый визуальный индикатор того.
что все выглядит нормально.
Следующей частью является программное обновление конечных точек Twilio webhook с нашим
публичный URL для ngrok.
Пришлось изрядно покопаться, чтобы найти нужные конечные точки в twilio-ruby
но как только я нашел их, дальше все пошло гладко.
Обновление с помощью API Twilio
Есть несколько вещей, которые нам понадобятся для обновления URL веб-крючков Twilio. Сайт
первое, что нам нужно, это клиент с нашими ключами.
Чтобы создать клиент Twilio, вам нужно получить ваш account_sid
и
там обязательно найти идентификатор службы вашего телефонного номера. Это необходимо для того, чтобы
выбрать правильный номер телефона для обновлений.
Обновления Twilio
account_sid = Rails.application.credentials.dig(:twilio)[:account_sid]
auth_token = Rails.application.credentials.dig(:twilio)[:auth_token]
development_incoming_phone_number = Rails.application.credentials.dig(:twilio)[:auth_token]
twilio_client = Twilio::Client.new(account_sid, auth_token)
twilio_client.api.v2010.accounts(account_sid).incoming_phone_numbers(developemnt_incoming_phone_number)
.update(sms_url: "#{ngrok_url}/webhooks/twilio",
sms_fallback_url: "#{ngrok_url}/webhooks/twilio_fallback",
voice_url: "#{ngrok_url}/webhooks/twilio_voice",
voice_fallback_url: "#{ngrok_url}/webhooks/twilio_voice_fallback")
puts "Webhook URLs updated!".green
rescue Twilio::REST::RestError => e
puts e
end
Я включил настройку клиента Twilio в задачу rake, но это было сделано в основном для того, чтобы иметь
пример, который будет работать как есть без дополнительной настройки. Обычно я
настраиваю клиента Twilio в инициализаторе, чтобы свести дублирование к минимуму.
пример файла config/initializers/twilio.rb
Twilio.configure do |config|
config.account_sid = Rails.application.credentials.dig(:twilio)[:account_sid]
config.auth_token = Rails.application.credentials.dig(:twilio)[:auth_token]
end
Когда клиент создан, мы можем использовать ngrok_url
в сочетании с конечной точкой
которая указывает на ваш обработчик веб-крючков, и обновить наши URL-адреса веб-крючков Twilio.
Если все идет по плану, мы выведем сообщение об успехе зеленым цветом.
Если все идет не так, как планировалось, мы спасаем Twilio::REST::RestError
и
выводим ошибку.
Теперь, всякий раз, когда нам нужно обновить URL-адреса веб-крюков Twilio (или
или любой другой сторонний сервис в будущем), мы можем выполнить это:
$ bin/rails development:set_twilio_webhooks
Поиск способа автоматически запускать ngrok в моем Procfile и иметь возможность
программно получить его публичный URL, стало отличной находкой для моего рабочего процесса. Это
то, что можно
можно распространить на множество других сервисов, с которыми вы, возможно, захотите работать в своей среде разработки
среде.
Сообщите мне о других сервисах, которые вы автоматизируете!