Добавить в корзинуПозвонить
Найти в Дзене

Python. Asterisk. Звоним через локальную АТС

Говорят, лень - двигатель прогресса. Конечно, свою лень можно дисциплинировать до состояния «лень будет переделывать», но это уже попахивает автоматизацией. Автоматизация ведет к перфекционизму, когда все части механизма отлажены и работают как часы. А перфекционизм в свою очередь - к лишнему стрессу. Ведь если что-то и может сломаться, оно обязательно обвалится. Что-то я немного увлекся с философией. Так вот, поговорим про лень и ошибки. Бывала ли когда-то у вас ситуация, что вы набирали номер телефона и ошибались одной цифрой? Ну признавайтесь, было же. А бывало такое, что вам лень набирать номер каждый раз? А вот у руководителя наших менеджеров возник такой вопрос: как бы сделать так, чтобы человек не набирал каждый раз номер клиента, а звонок осуществлялся бы по одному клику. Ну что же, давайте разбираться. В нашем распоряжении есть локальная АТС на Asterisk+Freepbx, золотые руки (вроде даже не из причинного места) и CRM система, в которой хранятся номера телефонов клиентов. Для на
Оглавление

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

Что-то я немного увлекся с философией. Так вот, поговорим про лень и ошибки.

Бывала ли когда-то у вас ситуация, что вы набирали номер телефона и ошибались одной цифрой? Ну признавайтесь, было же. А бывало такое, что вам лень набирать номер каждый раз? А вот у руководителя наших менеджеров возник такой вопрос: как бы сделать так, чтобы человек не набирал каждый раз номер клиента, а звонок осуществлялся бы по одному клику.

Ну что же, давайте разбираться. В нашем распоряжении есть локальная АТС на Asterisk+Freepbx, золотые руки (вроде даже не из причинного места) и CRM система, в которой хранятся номера телефонов клиентов.

Для начала вспомним, что все удаленное управление Asterisk происходит через Asterisk AMI.

Asterisk AMI (Asterisk Manager Interface)

Это сетевой интерфейс, который позволяет разработчикам взаимодействовать с Asterisk, открытым IP PBX-системой. AMI обеспечивает управление и мониторинг системы в реальном времени через протокол TCP.

С помощью Asterisk AMI можно:

  1. Управлять звонками — запуск, завершение или передача звонков.
  2. Управлять пользователями — добавление, удаление или изменение статуса абонентов.
  3. Мониторить события - получение уведомлений о входящих/исходящих звонках.

Приступим к настройке нашего Asterisk.

Для начала, нам нужно установить плагин Asterisk AMI (если его нет) и включить. По умолчанию, AMI будет использовать порт tcp 5038, так что он должен быть открыт на фаерволле АТС.

Заходим через web на наш FreePBX в Settings -> Asterisk Manager Users и создаем учетную запись, через которую мы будем обращаться к нашей АТС

-2

На данном этапе я предполагаю, что ваша АТС уже давно в работе, у вас настроены все маршруты и внутренние номера. Так что эту часть я пропущу.

Начинаем писать скрипт обращения к АТС

В работе мы будем использовать библиотеку asterisk-ami.

pip install asterisk-ami
  • Импортируем из библиотеки модуль AMIClient и создаем подключение к АТС.
-3
from asterisk.ami import AMIClient
client = AMIClient(address='192.168.1.170', port=5038, timeout=None)
client.login(username='Python_user', secret='strong_password')

Если все прошло успешно, то строка print(client) выдаст вам объект Ami client.

<asterisk.ami.client.AMIClient object at 0x73a5f4262a70>

  • Для совершения какого-либо действия, нам понадобится модуль SimpleAction. Создадим объект звонка.
-4
action = SimpleAction(
name='Originate',
Channel='SIP/101',
Exten='client_number',
Priority=1,
Context='ANY',
CallerID='Webcall',
)
call = client.send_action(action)

name - Обязательно 'Originate', так мы показываем Астериску, что мы хотим запустить звонок.

Channel - Номер, которому Астериск позвонит первым.

Exten - Номер, которому Астериск позвонит после того, как Channel поднимет трубку.

Context - Имя CustomContext, через который будет совершен звонок.

CallerID - Нужно для дальнейшей идентификации веб звонка, ни на что не влияет.

Важно: если в Channel, или Exten используется внутренний номер, он указывается в формате 'SIP/номер'

  • Добавим немного больше логики: хотелось бы, чтобы при звонке использовался не случайный CustomContext, а тот, который привязан к Extension менеджера.

Для этого нам нужно отправить на Астериск команду 'SIPshowpeer'.

-5
manager = 101
action = SimpleAction(
name='SIPshowpeer',
Peer=manager
)
ext = client.send_action(action)
context = ext.response.keys['Context']

На данном этапе мы уже можем вписывать номер менеджера и номер клиента, и совершать звонок.

Однако помним, мы должны получать эти номера от CRM. Договариваемся с программистами о том, что мы будем получать POST запрос и возвращать json объект о статусе звонка.

Подготавливаем среду для нашего веб звонка

  • Устанавливаем nginx с cgi модулями.
  • В /etc/nginx/conf.d создаем файл webcall.conf
server {
listen 80;
server_name example.com
location / {
gzip off;
root /var/www/webcalls;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/webcalls$fastcgi_script_name;
}
}
  • В папке /var/www создаем директорию webcalls и создаем в ней скрипт call.py
-6
import cgi
form = cgi.FieldStorage()
internal_number = form.getvalue('manager')
client_number = form.getvalue('client')

Так мы ожидаем, что при обращении к нашему скрипту, передастся два параметра: manager и client.

Обращение должно быть по URL http://example.com/call.py?manager=[номер менеджера]&client=[номер клиента]

  • В конец скрипта мы прописываем генерацию ответа нашего скрипта. Напомню, нам нужно отдавать json объект, так что импортируем в скрипт библиотеку json.
-7
print("Content-type: application/json\n")
response = {'statusCode': status_code}
print(json.dumps(response))
  • В нашем скрипте звонка добавляем проверку, был ли совершен звонок. Для этого после команды вызова звонка, добавляем получение его статуса.
-8
call_status = call.response.status
if call_status == 'Success':
status_code = 1
else:
status_code = 0
  • Собираем все это воедино и тестируем.
  • Получаем похвалу от менеджеров, которым теперь достаточно кликнуть кнопку в CRM, и АТС сама позвонит и на их внутренний номер, и клиенту.

Весь код выложил здесь