Миллионы устройств подключены к Интернету и генерируют данные — от холодильников, отслеживающих своё содержимое, до веб-камер, фиксирующих вторжения. Эти подключённые устройства, известные как «Интернет вещей», часто создают задачи, которые должны выполняться людьми. Как отслеживать всю эту работу? Как назначать её подходящему по навыкам и доступному сотруднику? Как узнать, когда она выполнена?
Для решения подобных проблем Twilio запустил TaskRouter — сервис для распределения задач между исполнителями, способными их выполнить.
В недавнем посте Грег Богес показал, как создать фотобудку на базе Arduino Yún. В этой статье мы возьмём фотографии, созданные фотобудкой, и создадим задачу, которую могут выполнить только люди, — придумывание забавных подписей.
Руководство по фотобудке завершалось загрузкой фотографий в Dropbox. Это руководство продолжает с того места, где оно закончилось.
— При загрузке нового файла в Dropbox создаётся задача
— Волонтёры отправляют SMS в систему и добавляются как исполнители
— Исполнители переводятся в статус «Ожидание», что означает их готовность к работе
— TaskRouter сопоставляет фотографию, требующую подписи, с доступным исполнителем. Наше приложение отправляет им фото через MMS
— Исполнитель помечается как «Занят», пока не пришлёт подпись
— После получения подписи статус исполнителя меняется обратно на «Ожидание», и он ждёт следующего задания
Мы создадим распределённое приложение для подписывания фотографий с использованием Ruby и Sinatra.
Вам не нужна полностью функционирующая фотобудка на базе Arduino, чтобы следовать этому руководству. Однако необходимо настроить приложение Dropbox. Инструкции можно найти в разделе «Arduino и Dropbox» в руководстве по фотобудке. После настройки приложения Dropbox вы можете имитировать работу фотобудки, вручную загружая файлы в Dropbox.
Помимо приложения Dropbox, вам понадобятся:
— бесплатный аккаунт Twilio
— Twilio-номер с поддержкой MMS (доступно только для номеров США и Канады)
— ngrok — бесплатное ПО для туннелирования IP
Ваш компьютер для разработки, скорее всего, скрыт за роутером и не имеет публично доступного IP-адреса. Однако и Dropbox, и Twilio должны отправлять HTTP-запросы к этому приложению, поэтому вам нужно создать туннель из публичного интернета к вашему локальному серверу.
Наш любимый способ сделать это — ngrok. Если вы ещё не установили ngrok, скачайте его и переместите в домашнюю директорию. Также зарегистрируйтесь на бесплатный аккаунт ngrok и следуйте инструкциям по настройке кастомных доменов. Это позволит не менять URL вебхуков в панелях управления Twilio и Dropbox каждый раз при перезапуске ngrok. Если хотите узнать больше о ngrok, ознакомьтесь с отличным руководством Кевина Уиннери.
После установки ngrok запустите его с кастомным субдоменом (например, вашим именем) и направьте на порт 9292:
Оставьте ngrok открытым в терминальном окне на всё время выполнения этого руководства.
Лучше всего начать создание приложения TaskRouter с панели управления TaskRouter. Приложения TaskRouter привязаны к рабочему пространству. Давайте создадим его:
— Нажмите «Создать рабочее пространство»
— Дайте рабочему пространству дружелюбное имя «Подписи для фотобудки»
— Оставьте шаблон как «Нет»
— Нажмите «Сохранить»
После создания рабочего пространства измените стандартное действие с «Офлайн» на «Ожидание». Мы объясним почему через несколько минут, но краткий ответ заключается в том, что мы хотим, чтобы наши исполнители были готовы получать задачи сразу после входа в систему.
Ключевым свойством здесь является «Целевые исполнители», которое указывает, что исполнители, имеющие право выполнять задачи в этой очереди, должны обладать навыком «подпись». Для целей этого руководства у нас будет только один тип исполнителей, но очередь задач действительно начинает сиять, когда у вас есть множество типов задач, требующих различных навыков. После завершения этого руководства вы будете в отличной позиции для создания чего-то более сложного.
После настройки очереди задач нажмите «Сохранить».
Далее нам нужно создать рабочий процесс, который будет направлять задачи в нашу очередь задач. Нажмите «Рабочие процессы» в верхней части панели управления, затем нажмите «Создать рабочий процесс». Настройте его со следующими свойствами:
— Дружелюбное имя: Рабочий процесс фотобудки
— Callback назначения: http://example.ngrok.com/assignment (замените `example` на ваш субдомен ngrok)
— Оставьте поле URL резервного callback назначения и тайм-аут резервирования задачи пустыми
— Оставьте «Очередь подписей» как очередь задач по умолчанию
— Нажмите «Сохранить»
По умолчанию рабочий процесс будет помещать задачи в очередь подписей благодаря настройке очереди задач по умолчанию. Если мы хотим быть более явными в этом, чтобы подготовиться к более надёжной системе, мы можем создать фильтр в разделе конфигурации маршрутизации. Давайте настроим фильтр для наших задач подписывания. Нажмите кнопку «Добавить фильтр» и установите следующие свойства:
— Метка фильтра: Фильтр подписей
— Выражение: `required_skill = «caption»`
— Целевая очередь задач: Очередь подписей
— Приоритет: 1
С этим фильтром задача с `required_skill`, установленным в «caption» в её атрибутах, будет направлена в очередь подписей. Ваша конфигурация маршрутизации должна выглядеть так:
Нажмите «Сохранить», чтобы завершить создание рабочего процесса. Это вся настройка, которую нам нужно сделать на панели управления. Давайте перейдём к коду.
Наше приложение будет написано на Ruby с использованием Sinatra. Давайте создадим директорию для нашего приложения и несколько файлов, которые нам понадобятся для начала:
Затем отредактируйте Gemfile:
Установите bundler, если вы ещё этого не сделали:
Затем установите гемы:
Вместе с гемами для Dropbox и Twilio мы включили Envyable — гем для управления переменными окружения. (Чтобы узнать больше об этом, прочитайте отличный пост Фила Нэша о управлении переменными окружения в Ruby).
Для использования `envyable` нам нужно создать директорию `config` и файл `env.yml`:
Откройте env.yml и добавьте следующий YAML:
Скопируйте значения для вашего Twilio Account SID, Twilio Auth token — их можно найти, нажав «Показать учётные данные» в правом верхнем углу панели управления рабочим пространством. Затем скопируйте Workspace SID и Workflow SID — их можно найти на соответствующих страницах. Затем вставьте номер одного из ваших Twilio-номеров с поддержкой MMS.
Для токена Dropbox посетите консоль приложений Dropbox и перейдите в созданное ранее приложение. В разделе OAuth 2 нажмите `Generate` под «Сгенерированный токен доступа» и скопируйте полученный токен в YAML.
С нашим env.yml переменные окружения теперь будут доступны через `ENV[‘NAME_OF_VARIABLE’]`.
Теперь давайте начнём работу над нашим приложением Sinatra. Откройте `app.rb`, вставьте эти строки и сохраните файл.
Наконец, отредактируйте config.ru, который сообщает нашему серверу, что делать при запуске `rackup`.
Если вы хотите проверить, работает ли это на данный момент, попробуйте запустить сервер и убедитесь, что не возникает ошибок:
Наше приложение будет использовать вебхук Dropbox для получения уведомлений при загрузке файлов. Это позволяет нам создавать задачи для нашего приложения по мере поступления фотографий. Однако перед использованием вебхука нам нужно подтвердить наше приложение в Dropbox.
Для процесса подтверждения Dropbox отправит GET-запрос к нашему вебхуку с параметром `challenge`. Наш HTTP-ответ должен просто включать текст этого вызова.
Создайте новый маршрут в `app.rb` для обработки этого запроса:
После нажатия «Добавить» Dropbox подтвердит наш домен. Мы могли бы удалить маршрут GET /dropbox после этого, но если мы когда-нибудь изменим домены (например, развернём в продакшене), нам снова потребуется повторное подтверждение. Так что лучше оставить его.
Если вы хотите узнать больше об этом процессе авторизации или о взаимодействии с API Dropbox в целом, ознакомьтесь с их хорошо написанной документацией по API.
Когда фотография загружается, Dropbox отправляет POST-запрос на наш вебхук `/dropbox` (в дополнение к GET /dropbox, который мы использовали для подтверждения приложения). Информация, предоставляемая в POST-запросе, довольно ограничена. Она содержит только массив идентификаторов пользователей, у которых есть новые изменения файлов в настроенном приложении Dropbox, но не содержит дополнительной информации о самой загрузке файла.
Поскольку вебхук не сообщает нам, какие файлы были добавлены, нам нужно запросить список последних изменений в Dropbox через их метод `delta`. Чтобы убедиться, что мы не получаем дублирующиеся изменения, нам нужно сохранить «курсор», возвращаемый нам Dropbox, и передавать его обратно при последующих вызовах `delta`. Ради быстроты в этом руководстве мы сделаем это неправильно и сохраним курсор в глобальной переменной. Пожалуйста, используйте надлежащее хранилище данных в реальном приложении.
Ниже `Envyable.load(‘./config/env.yml’, ‘development’)` в `app.rb` добавьте следующее:
Теперь мы создадим маршрут `post /dropbox`, который будет:
— создавать REST-клиент с использованием нашего токена доступа Dropbox
— получать список изменений в нашей папке Dropbox с момента последнего курсора
— сохранять новый курсор
Затем он будет перебирать каждый файл в списке изменений и:
— получать его имя файла
— запрашивать общедоступный URL у Dropbox с помощью нашего REST-клиента
— создавать новую задачу в TaskRouter (мы оставим здесь заглушку на данный момент)
И наконец, он вернёт 200 — иначе Dropbox будет повторять запрос снова и снова.
Вот код:
Если вы хотите узнать больше о том, что мы здесь сделали, ознакомьтесь с основной документацией API Dropbox.
Нам предстоит много работать с Twilio, поэтому давайте создадим файл `twilio_helpers.rb`, чтобы наш код оставался чистым:
Теперь давайте создадим вспомогательный метод в `twilio_helpers.rb` для создания клиента REST API TaskRouter:
Затем подключим twilio helpers в наш `app.rb`:
Мы будем использовать наш клиентский хелпер для создания новой задачи с image_url в качестве атрибута. Замените комментарий `# create task` следующим:
Давайте протестируем то, что мы создали на данный момент. Перезапустите ваш сервер Sinatra и загрузите файл в Dropbox — либо через вашу фотобудку, либо просто перетащив изображение в папку вашего приложения Dropbox.
После загрузки файла сработает вебхук и обратится к маршруту `/dropbox`, который затем создаст задачу в TaskRouter. Откройте панель управления TaskRouter и перейдите на страницу задач. Вы должны увидеть новую задачу. Если вы нажмёте на задачу, то увидите `image_url`.
Теперь, когда мы можем создавать задачи, нам нужны исполнители, которые смогут их выполнять.
Исполнители будут присоединяться к системе, отправляя SMS на наш номер Twilio. Нам нужно настроить вебхук, который Twilio будет использовать при получении нового сообщения. Откройте список номеров на панели управления Twilio, нажмите на номер телефона, который вы ранее ввели в env.yml, и настройте номер, установив URL запроса сообщений на `http://<ваш_субдомен>.ngrok.com/message`.
В рамках этого поста мы сосредоточимся на двух сценариях, когда кто-то отправляет SMS:
— Они отправляют SMS впервые. Мы создадим исполнителя, используя их номер телефона в качестве дружественного имени.
— Они предоставляют подпись. Мы сохраним её, а затем переведём исполнителя в статус готовности к получению новых задач.
Прежде чем создать маршрут для обработки вебхука, давайте создадим ещё два вспомогательных метода в `twilio_helpers.rb`.
Сначала метод для проверки существования исполнителя по заданному номеру телефона:
Затем метод для упрощения генерации TwiML-ответов, которые мы будем использовать для ответа людям, когда они отправляют SMS в систему:
Теперь вернёмся к `app.rb` и создадим конечную точку `/message`.
Пока сосредоточимся на первом случае использования: кто-то отправляет SMS, и исполнитель с таким номером ещё не существует:
В этом случае мы создадим нового исполнителя с:
— атрибутом, определяющим его номер телефона
— дружественным именем, установленным как его номер телефона, чтобы его было легче идентифицировать
Также ответим текстовым сообщением с просьбой подождать и дождаться фотографии.
Давайте протестируем это. Перезапустите ваш сервер, затем отправьте текстовое сообщение на ваш номер Twilio. После получения ответа проверьте вкладку исполнителей на панели управления TaskRouter. Вы должны увидеть нового исполнителя с вашим номером телефона в качестве дружественного имени.
Однако происходит что-то ещё. Если вы посмотрите на ваш сервер, то увидите, что TaskRouter попытался сделать HTTP-запрос на /assignment, но мы ещё не определили этот маршрут. Давайте сделаем это сейчас.
Когда в системе есть задача и свободный исполнитель, обладающий необходимыми навыками, TaskRouter начинает творить чудеса. Когда TaskRouter видит потенциальное совпадение, он делает HTTP-запрос к вебхуку назначения, определённому на нашей панели управления рабочим процессом. Этот HTTP-запрос отправляет информацию о задаче и спрашивает, хотите ли вы, чтобы исполнитель принял её.
В этом запросе у нас есть всё необходимое для отправки задачи исполнителю: image_url и номер телефона исполнителя.
Давайте создадим маршрут, который будет:
— отвечать на POST-запрос по адресу /assignment
— извлекать `phone_number` из `worker_attributes`
— извлекать `image_url` из `task_attributes`
— сохранять image_url для последующего использования
— вызывать twilio_helper с именем `send_photo`, который мы определим через секунду
— возвращать JSON-инструкции TaskRouter о том, что исполнитель принимает задачу
Нам также нужно хранить данные о наших URL-адресах изображений и подписях. Мы не будем рассказывать, как это сделать в этом посте. Вы можете использовать MySQL, DynamoDB или любое другое хранилище по вашему выбору. Для целей этого поста мы просто оставим комментарий там, где вы бы сохраняли данные, которые хотите сохранить.
Создайте маршрут для обработки назначения:
Первые четыре строки извлекают image_url и phone_number из параметров, отправленных нам TaskRouter. Затем мы отправляем фото с помощью Twilio-хелпера, который определим через секунду. Последние две строки возвращают JSON, сообщающий TaskRouter, что наш исполнитель принимает задачу.
Теперь давайте создадим наш метод `send_photo` в twilio_helper.rb:
У нас есть всё необходимое для назначения задачи исполнителю и отправки ему изображения для подписи. Давайте попробуем это.
Нам нужно, чтобы ваш номер телефона был «новым» исполнителем для работы этого механизма, поэтому вернитесь в панель управления, нажмите на созданного ранее исполнителя, переключите его активность на «Офлайн» и затем удалите его.
Затем перезапустите ваш сервер, чтобы загрузить изменения, которые мы только что внесли. После этого отправьте SMS на ваш номер Twilio ещё раз, и наше приложение ответит вводным текстом, как в прошлый раз.
Теперь TaskRouter делает POST-запрос к вашему недавно созданному маршруту /assignment. Вы можете наблюдать за этим, открыв localhost:4040 в браузере. Этот маршрут отправит MMS с фотографией из Dropbox на ваш телефон.
Мы создали исполнителя в статусе «Ожидание», и он только что получил своё первое задание по подписыванию. Что происходит, когда он отправляет ответ? После того как мы сохраним подпись исполнителя, мы переведём его обратно в активность «Ожидание», чтобы он мог получать новые фотографии для подписывания.
Давайте создадим Twilio-хелпер для получения исполнителя по его номеру телефона. В `twilio_helper.rb`:
Создадим ещё один хелпер для получения SID активности «Ожидание»:
А затем используем эти два метода для изменения активности исполнителя обратно на «Ожидание»:
С этими хелперами мы можем отвечать на входящие сообщения от существующего исполнителя. В конечной точке `/message` файла `app.rb` добавим следующий код в блок `if worker_exists?`, к которому мы обещали вернуться:
Это весь код для этого приложения. Перезапустите ваш сервер, чтобы загрузить изменения. Затем отправьте забавный текст на ваш номер Twilio. Вы получите ответ с благодарностью, и ваша активность в TaskRouter будет переключена обратно на «Ожидание». Если в очереди задач есть ещё задачи, TaskRouter сделает ещё один POST-запрос к маршруту /activity, и ваш телефон загорится новой фотографией. Вы ответите забавной подписью, и так далее.
Давайте подведём итоги. В этом посте мы:
— Создали новое рабочее пространство, рабочий процесс и очередь задач в TaskRouter
— Создавали задачи в ответ на загрузку в Dropbox
— Позволили волонтёрам регистрироваться как исполнители через SMS
— Назначали фотографии для подписывания исполнителям
— Обновляли статус исполнителя после завершения задачи
TaskRouter предоставил нам прочную основу для нашего приложения, которое легко расширяется для ещё более разнообразного набора задач среди исполнителей с различными навыками. Рассмотрите возможность расширения того, что мы построили в этом посте, следующими предложениями:
— Создайте специализированных авторов подписей (например, некоторые люди могут лучше справляться с подписями к фотографиям с свадебной фотобудки, а другие — с фотографиями с офисных вечеринок).
— Создайте вторую очередь задач для людей, которые могут оценивать подписи (Интернет хорош в количестве, но нам может понадобиться контроль качества).
— Постройте веб-сайт для демонстрации этих забавных подписей.
Я очень рад видеть, что вы создадите с помощью TaskRouter. Если у вас возникнут вопросы во время создания вашего приложения, пожалуйста, свяжитесь со мной по электронной почте brent@twilio.com или напишите мне в Twitter @brentschooley.