Поступила задача связать Asterisk c неизвестной CRM системой. Среди прочего требовалось отправлять POST запрос в формате JSON в момент ответа оператором на входящий звонок. В момент, когда оператор поднимает трубку - необходимо в CRM открывать для него карточку клиента/заказа.
Итак, можно было бы отправлять запрос напрямую из диалплана. Нам этот вариант не подойдет, но возможность эту мы рассмотрим. В случае с Dial, а параметрах указываем ссылку на контекст, в который необходимо перейти при ответе и передаем туда необходимые переменные. Как-то так
same => n,Dial(SIP/100,60,iU(answer-script,${CALLERID(num)}))
и далее в контексте делаем, что нам нужно:[answer-script]
exten => s,1,NoOp(Выполняем действия с входящим номером ${ARG1})
same => n,Return
В случае с очередью можно было бы поступить как-то так:;идем в очередь на 60 секунд и при ответе перемещаемся в answer-script
same => n,Queue(queue,tT,,,60,,,answer-script^${CALLERID(num)})
Но в моем случае данные данные способы не подходят, так как не получается выловить номер конкретного ответившего оператора. К тому-же это решение не совсем удобное при большом количестве входящих звонков. Нам нужен отдельный сервис, который будет слушать события Asterisk и при нахождении нужных нам выполнять какие-то действия.
События слушать будем через Asterisk Managment Interface (AMI) и при нахождении необходимого, отправляем данные POST запросом. Для этого напишем скрипт на Python и сделаем из него сервис.
Не забудьте сначала создать пользователя для работы с AMI, например таким образом (у меня он с полными правами, вы может ограничить только чтением событий):
nano /etc/asterisk/manager.conf
[general]
enabled = yes
port = 5038
bindaddr = 0.0.0.0
allowmultiplelogin = yes ;несколько параллельных подключений с одним именем
[crm]
secret = pass
deny = 0.0.0.0/0.0.0.0
permit = 127.0.0.1/255.255.255.0
read=system,call,log,verbose,command,agent,user,config,command,dtmf,reporting,cdr,dialplan,originate
write=system,call,log,verbose,command,agent,user,config,command,dtmf,reporting,cdr,dialplan,originate
Устанавливаем зависимости:
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python3 get-pip.py
pip3 install panoramisk
или в моем случае с Ubuntu
apt-get install python3-panoramisk
apt install python3-requests
Открываем доступ до AMI
nano /etc/asterisk/manager.conf
Путем просмотра событий в момент ответа на звонок выяснилось, что нам необходим Event: BridgeEnter. В нем есть вся необходимая информация: на какой транк пришел вызов, какой оператор ответил, какой был номер клиента. В событии нам необходимо проверять следующие значения:ChannelStateDesc: Up - оператор поднял трубку
ConnectedLineNum: 100 - внутренний номер ответившего оператора
Exten: 7XXXXXXXXXX - наш внешний номер (имя транка)
CallerIDNum: 7XXXXXXXXXX - номер звонящего клиента
Context: incoming-queue - контекст, куда пришел вызов (это необходимо учитывать, чтобы не было лишних срабатываний, к примеру при локальных звoнкаx)
Мы возьмем библиотеку Panoramisk для доступа в AMI. Скрипт получается вот таким:
nano /etc/asterisk/scripts/crm_callback.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-import asyncio
from panoramisk import Manager
import threading, queue
import requests, json
import pickle
# API метод CRM
url = 'https://crm-name.ru/call'
# данные для доступа к AMI
ip = '127.0.0.1'
username = 'crm'
secret = 'pass'
exiting = False
manager = Manager(loop=asyncio.get_event_loop(),
host=ip,
username=username,
secret=secret)
# Смотрим событие BridgeEnter c необходимыми полями
@manager.register_event('BridgeEnter')
def BridgeEnter(manager, message):
global q
# указываем контексты для звонка
if (message.Context not in ('incoming-queue', 'incoming')):
return
# нам нужно, чтобы значение ChannelStateDesc было Up
if (message.ChannelStateDesc != 'Up'):
return
# выгребаем необходимые данные
data = {
'our_external_trunk' : message.Exten,
'our_internal_number' : message.ConnectedLineNum,
'client_phone_number' : message.CallerIDNum
}
q.put(data)
# отправка POST запроса в CRM
def crm_post(data): headers = {'Content-type': 'application/json', # Определение типа данных
'Accept': 'text/plain',
'Content-Encoding': 'utf-8'}
d1 = json.dumps(data)
# выводим получившийся запрос
print(d1)
# отправляем
answer = requests.post(url, data=d1, headers=headers)
# тред и очередь сделаны для отправки POST запросов,
# чтобы от Asterisk данные принимать без задержек
def post_thread():
global q, exiting
while not exiting:
data = q.get()
if (data == 'exit'): break
try:
crm_post(data)
except:
print('error post')
q = queue.Queue()
threading.Thread(target=post_thread).start()
def main():
manager.connect()
try:
manager.loop.run_forever()
except KeyboardInterrupt:
exiting = True
q.put('exit')
manager.loop.close()
if __name__ == '__main__':
main()
Делаем исполняемым chmod +x /etc/asterisk/scripts/crm_callback.py
Можете проверить работу скрипта запустив его. Если все работает, создадим сервис из него с помощью systemd nano /lib/systemd/system/crm-asterisk-callback.service
[Unit]
Description=crm calls post service
Wants=network-online.target
After=network.target network-online.target
[Service]
WorkingDirectory=/etc/asterisk/scripts/
Type=idle
ExecStart=/etc/asterisk/scripts/crm_callback.py
KillMode=control-group
Restart=always
RestartSec=10
StandardOutput=null
[Install]
WantedBy=multi-user.target
Далее добавляем в автозагрузку и запускаем сервис
sudo systemctl enable crm-asterisk-callback
sudo systemctl start crm-asterisk-callback
Комментариев нет:
Отправить комментарий