Создание менеджера офисных телефонов с помощью Django, Heroku и Twilio

Автор: | 28.07.2026

Частое желание, которое я нередко слышу от офис-менеджеров, — это потребность заменить всю офисную телефонную систему набором «рабочих» телефонных номеров, которые можно программировать на звонки на личные или рабочие телефоны сотрудников. Это избавило бы их от необходимости инвестировать в настольные телефонные аппараты и привязываться к долгосрочным контрактам. Они также хотят иметь возможность легко менять логику обработки при звонках на эти номера. Люди часто хотят использовать для этого Twilio, и меня часто спрашивают, возможно ли это. Простой ответ на этот вопрос — да.

Более сложный ответ: Да… если у вас есть команда разработчиков, которые смогут создать для вас подобную систему. Многие из этих людей просто хотят получить готовое решение, но не знают, как реализовать его с помощью набора функций, которые предоставляет Twilio. Мне стало очевидно, что демонстрация того, как создать менеджер офисных телефонов с помощью Twilio, будет весьма полезной. Желая немного размять свои инженерные навыки, я выделил время на создание версии с открытым исходным кодом, которую люди могли бы использовать или изучать, чтобы создавать собственные решения.

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

— Создавать расписания «рабочих часов» для вашего персонала.

— Создавать новые действия при поступлении звонка на номер. Эти действия представляют собой обычные шаблоны TwiML, вам нужно лишь заменить в них ключевые части.

— Настраивать номер Twilio для использования расписания и набора действий.

Затем этот номер можно будет раздавать и использовать в качестве профессионального номера для одного сотрудника, службы голосовой почты или даже системы голосового меню для более крупной организации.

Вы можете либо следовать этому руководству и создать собственное приложение (используя другой веб-фреймворк или язык программирования), либо загрузить версию проекта с открытым исходным кодом, которую мы будем разрабатывать, на GitHub здесь: https://github.com/phalt/hermes

Что вы узнаетеВ этом посте мы:

— Опишем компоненты, входящие в это приложение.

— Рассмотрим варианты проектирования технической реализации и определим необходимые для нее компоненты.

— Настроим базовый проект Django с использованием шаблона.

— Покажем, как создать необходимые компоненты в Django.

— Создадим и применим миграции схемы базы данных для этих компонентов.

— Напишем базовую логику для конечной точки Twilio.

Вам понадобятся следующие инструменты:

— Git

— Python

— pip

— Virtualenv

— Доступ к глобальной сети Интернет

Приложение, которое поможет вам создать эта серия статей, будет иметь довольно сложную структуру данных. К счастью для нас, используемый нами веб-фреймворк Django имеет набор API для управления структурой данных и ее абстрагирования, что позволяет нам использовать высокоуровневый Python для управления данными по своему усмотрению. Нам не придется написать ни единой строки SQL.

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

*Каждый телефонный номер можно настроить с помощью набора действий, которые выполняются на основе рабочего расписания. Каждое действие состоит из шаблона TwiML и некоторых параметров. Каждое расписание имеет время начала и окончания, а также дни недели. Каждая конфигурация будет связана с именем, телефонным номером, расписанием, действием «в рабочее время» и действием «вне офиса».*

*При звонке на телефонный номер конфигурация обратится к расписанию, чтобы определить, какое действие и какой TwiML использовать для ответа.*

Это явное описание того, что мы хотим, чтобы менеджер офисных телефонов мог делать. Я взял на себя смелость выделить в этом описании компоненты, которые можно превратить в объектные модели в нашем приложении Django:

**Действие (Action)** – Шаблон TwiML с некоторыми настраиваемыми параметрами. **Расписание (Schedule)** – Время начала и окончания, а также набор дней. Если текущее время находится между временем начала и окончания и совпадает с определенным днем, расписание считается «активным», в противном случае — «вне офиса». **Конфигурация (Configuration)** – Связывает номер Twilio с расписанием, а также с «активным» действием и действием «вне офиса».

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

Другим ключевым моментом является то, как Twilio будет взаимодействовать со всеми Конфигурациями, Расписаниями и Действиями при звонке на телефонный номер. Я составил блок-схему, чтобы описать поток выполнения этой функции:

Приложению потребуется лишь одна конечная точка, или URL-адрес, на который Twilio будет отправлять запрос. Когда эта конечная точка получает запрос TwiML от Twilio, первое, что нужно сделать, — убедиться, что вызываемый телефонный номер уже настроен в нашем приложении. Если нет, мы просто отклоняем вызов с помощью тега TwiML . Если номер настроен, текущее время сравнивается со временем начала, временем окончания и днем недели в компоненте Расписание, связанном с этой Конфигурацией. В зависимости от времени и даты вычисляется либо стандартное Действие из шаблона, либо Действие «вне офиса». Наконец, соответствующий TwiML из нужного Действия возвращается обратно в Twilio.

Настройка проектаВ идеальном мире эта статья была бы огромным подробным пошаговым руководством, которое подарит вам часы удовольствия. Однако вероятно, что многое из того, что было бы там описано, вам уже известно или может показаться обыденным. Чтобы исключить большую часть настройки проекта и позволить нам сосредоточиться на создании интересных элементов, я подготовил для вас шаблонный проект, в котором уже настроены некоторые его части (например, модель Actions, которая весьма сложна). Код, который мы собираемся добавить, создает модели Configuration и Schedule, а также конечную точку Twilio.

Для начала используйте git, чтобы клонировать репозиторий на свой локальный компьютер. Для этого откройте терминал, перейдите в нужный каталог и введите следующие команды:

Это создаст локальную клонированную копию шаблонного приложения на вашем компьютере. Поскольку это проект на Python, передовой практикой является использование Virtualenv для создания изолированной среды разработки:

Виртуальное окружение можно активировать с помощью:

Наконец, все зависимости можно установить с помощью pip:

Это займет 1-2 минуты. После этого мы будем готовы начать писать код.

Создание моделей DjangoМы описали компоненты, необходимые для менеджера офисных телефонов, теперь давайте создадим их в Django. Откройте файл **configurations/models.py**, чтобы увидеть код для моделей Actions и Parameter. Модель Parameter используется моделью Action. Добавляемый код, модели Configuration и Schedule, должен начинаться со строки 11 между двумя комментариями.

Модель Configuration — это компонент верхнего уровня в приложении — она связывает все остальное воедино. Это означает, что она будет иметь множество внешних ключей к другим моделям в приложении. Желаемая модель Configuration должна выглядеть так:

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

— name – имя этой Конфигурации, для удобства пользователей.

— number – связь по внешнему ключу с TwilioNumber, еще одной объектной моделью, которую вы можете найти в файле twilio_numbers/models.py. Этот компонент хранит ссылки на телефонные номера Twilio, чтобы сократить количество HTTP-запросов к Twilio.

— action – связь по внешнему ключу со стандартным Действием для этой Конфигурации.

— oof_action – связь по внешнему ключу с другим Действием, которое вызывается, когда время выходит за рамки желаемого Расписания. Это странное слово, oof должно быть аббревиатурой от Out Of oFfice (вне офиса).

— schedule – связь по внешнему ключу с Расписанием для этой Конфигурации.

— active – логическое поле для определения, активна ли эта Конфигурация или нет. Это позволит пользователям отключать Конфигурации, когда они не хотят их использовать, не удаляя их.

Следующая объектная модель, которая нам нужна — это компонент Schedule. Ее можно разместить прямо под только что написанной моделью Configuration:

Здесь все немного отличается от модели Configuration, так что давайте все рассмотрим. Здесь есть еще один атрибут name, чтобы люди могли понять этот объект. Есть атрибуты start_time и end_time, которые являются полями TimeField. Все это кажется довольно стандартным.

Атрибут days — это поле ManyToMany для новой объектной модели Day, которой еще не существует. Давайте быстро добавим этот компонент над моделью Schedule, и тогда его важность станет более очевидной. Мы помещаем его выше, потому что Python — интерпретируемый язык, и ему нужно загрузить класс Day в память перед тем, как ссылаться на него.

Этот компонент очень простой, но очень важный. Django и Python не предоставляют встроенного способа выбора конкретных дней недели, т.е. — понедельник, вторник и т.д. Вы можете выбрать конкретные даты по стандарту ISO 8601, но это не очень удобно для этого приложения. Таким образом, наше приложение сможет использовать читаемые человеком дни недели и предоставить гораздо более простой интерфейс, который не зависит от конкретных дат.

Теперь вернемся к модели Schedule. Эта модель включает очень важный метод под названием on_call:

Этот метод сначала определяет текущее время и день недели с помощью модуля datetime в Python. Метод isoweekday() из datetime вернет число от 1 до 7 в зависимости от дня недели, например: 1 — понедельник, 7 — воскресенье. Метод on_call завершается поиском по списку связанных дней. Если сегодняшний день есть в расписании, он вернет True, если текущее время находится между start_time и end_time, в противном случае он вернет False. Этот метод будет использоваться позже для определения того, какое Действие будет применяться при поступлении звонка через Twilio.

Чтобы доказать, что это работает, мы написали несколько тестов в полной версии здесь. Эти тесты проверяют определенные даты и время, чтобы убедиться, что результаты возвращаются корректно.

МиграцияТеперь, когда все компоненты для этого приложения созданы, следующим шагом является создание файлов миграции схемы базы данных. Начиная с Django 1.7, это встроенный инструмент, поэтому ничего дополнительно устанавливать не нужно. Команда, необходимая для создания миграций:

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

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

Это должно привести к следующему результату (или аналогичному):

Создание конечной точки Twilio

С созданными в приложении объектными моделями можно создать описанную ранее конечную точку Twilio, которая будет возвращать желаемый TwiML на основе Конфигурации, Расписания и Действия.

Поскольку эта логика не связана напрямую с конфигурациями, мы должны держать ее в отдельном файле, поэтому откройте файл hermes/views.py. Описанная ранее блок-схема может быть переведена в следующий код на Python, который можно добавить в конец файла:

Это выглядит совсем иначе, чем блок-схема, но следует той же логике. Эта функция использует декоратор *@twilio_view* из Django-twilio, который будет обрабатывать большую часть форматирования HTTP, чтобы нашей логике приложения не пришлось этого делать. Параметры Twilio извлекаются из входящего запроса с помощью функции decompose из Django-twilio и сохраняются в переменной twilio_params. Также создается новый экземпляр Twilio Response и сохраняется в *twilio_response*.

В строке 6 выполняется быстрая проверка, чтобы убедиться, что входящий запрос Twilio является голосовым вызовом, поскольку менеджер офисных телефонов поддерживает только голос. Затем функция проверяет, существует ли модель Configuration, в которой телефонный номер совпадает с номером, с которого сейчас поступает запрос на конечную точку. Если она существует, выполняется еще одна проверка, чтобы узнать, находится ли расписание в режиме on_call, используя метод, разработанный ранее в этой статье. Если это так, стандартное действие вызывает метод *get_twiml()* (который вы можете увидеть здесь), который возвращает правильный TwiML. Если расписание не в режиме on_call, возвращается действие «вне офиса» или *oof_action*.

Если в любой из этих проверок происходит сбой, в Twilio отправляется стандартный ответ об отклонении вызова.

Что дальше?На этом заканчивается первая часть из серии статей по созданию менеджера офисных телефонов.

Эта статья помогла вам создать базовые объектные модели, необходимые в приложении. Она предоставила базовый шаблон для начала работы и даже описала логику, стоящую за сложной конечной точкой Twilio. Мы пока не сможем запустить приложение, но на данном этапе оно еще не делает ничего особенного, так что в этом нет большого смысла.

Но что дальше? Во-первых, целевой аудиторией этого приложения не являются разработчики, поэтому сейчас они вообще не могут им пользоваться, так как не создано никаких представлений (views) или форм. С помощью простых представлений или форм нетехнические пользователи смогут создавать, читать и обновлять новые Конфигурации, Расписания и Действия. О том, как создавать представления и формы для этого приложения, будет рассказано в следующем посте, но вы можете попробовать создать свои собственные прямо сейчас, если не можете ждать.

Еще одна вещь, которой здесь пока не хватает — как телефонные номера Twilio узнают, что им нужно направлять свои Voice URL на это приложение? На данный момент пользователю придется зайти на twilio.com и сделать это вручную. Это не очень эффективно, но с помощью REST API можно обновить конфигурацию номера. В следующем посте будет представлена логика для автоматического выполнения этого действия при каждом сохранении Конфигурации.

И наконец: приложение нужно где-то разместить, поэтому руководство по развертыванию этого приложения на Heroku станет отличным завершением следующего поста.