Pull to refresh
70.28
Слёрм
Учебный центр для тех, кто работает в IT

Asterisk: использование AEL в повседневной жизни

Reading time 5 min
Views 13K
image

Сегодня поговорим об интеграции Asterisk'a с CRM системой. Со стороны CRM вопрос рассматривать не будем, достаточно знать, что CRM хочет знать о всех звонках (как входящих, исходящих, так и переведенных)

Что мы хотим получить:

Для каждого звонка должны отправляться в CRM 2 события: start и stop. Естественно с кучей аргументов.

Для этого будем вызывать sh скрипт:

System(/etc/asterisk/scripts/crm.sh start ${GROUP} ${DSTNUM} ${CIDNUM} ${ID}); Начало звонка
System(/etc/asterisk/scripts/crm.sh stop ${GROUP} ${DSTNUM} ${CIDNUM} ${ID} ${CDUR}); Окончание

Содержимое самого скрипта нам неинтересно, смысл лишь в том, что он передает эти аргументы уже самой CRM, а иногда даже что-то возвращает обратно.

${GROUP}  - Имя отдела/организации
${DSTNUM} - Номер, на который осуществляется звонок (используется для вывода popup окна у менеджера)
${CIDNUM} - Номер звонящего
${ID}     - ID звонка (будет нас сопровождать на протяжении всего времени, пока вызывающий не повесит трубку). В качестве ID будем использовать ${UNIQUEID} самого первого вызова
${CDUR}   - Время разговора

Эти два скрипта мы запишем в макрос (кстати макросы в ael работают/вызываются немного иначе чем в extensions.conf, для этого используется команда Gosub. Об этом нужно знать, если хочешь вызвать макрос, например, в команде Dial в момент поднятия трубки абонентом B. Т.е. использовать не M(x) как в extensions.conf, а U(x))

macro crm (GROUP, DSTNUM, CIDNUM, ID) {
    System(/etc/asterisk/scripts/crm.sh start ${GROUP} ${DSTNUM} ${CIDNUM} ${ID});
    return;
}
macro crm-end (GROUP, DSTNUM, CIDNUM, ID) {
    MSet(CDUR=${CDR(billsec)});
    System(/etc/asterisk/scripts/crm.sh stop ${GROUP} ${DSTNUM} ${CIDNUM} ${ID} ${CDUR});
    return;
}

Для организации исходящих звонков задача оказалось элементарной, приведу лишь листинг, останавливаться тут не будем

context outgoing {
    _X. => {
        Set(GROUP=office1);
        Set(DSTNUM=${EXTEN});
        Set(CIDNUM=${CALLERID(num)});
        Set(ID=${UNIQUEID});
        &record-crm(${STRFTIME(,,%G/%m/%d/)}${ID});
        &crm(${GROUP},${DSTNUM},${CIDNUM},${ID});
        Dial(SIP/trunk/${DSTNUM});
    };
}

Правила обработки входящих звонков немного интересней. Встречает нас приветствие с предложением набрать добавочный номер, если номер не набран, то отправляем звонок в CRM:

context inc {
    2758725 => {
        MSet(DSTNUM=${EXTEN});
        MSet(GLOBAL(GROUP)=office1);
        Gosub(inc-crm,s,1(${EXTEN},${CALLERID(num)},${UNIQUEID})) ;
    };
}
context inc-crm  {
    s => {
        MSET(DSTNUM=${ARG1});
        MSET(GLOBAL(CIDNUM)=${ARG2});
        MSet(GLOBAL(ID)=${ARG3});
        Background(welcome_message);
        &crm(${GROUP},${DSTNUM},${CIDNUM},${ID});
        Gosub(redirect,${DB(crm/key)},1);
    };
}

Контекст inc нам нужен на случай, если у нас больше, чем один номер для входящих звонков, часть переменных назначаем глобально, так как они нас будут сопровождать в разных частях диалплана.
&crm(${GROUP},${DSTNUM},${CIDNUM},${ID});
Отправляем первый запрос в CRM. Если клиент постоянный, то CRM вернет номер ответственного менеджера, и наш скрипт запишет его в AstDB (asterisk -rx «database put crm key XXX»). Если клиент новый или ему не назначен ответственный менеджер, то CRM вернет номер группы/очереди (это и доставило немного хлопот в плане передачи start/stop запросов)
Получив заветную комбинацию цифр из CRM, мы отправляемся дальше.

context redirect {
    1000 => {
        &record-crm(${STRFTIME(,,%G/%m/%d/)}${ID});
        &crm-group(${QUEUE_MEMBER_LIST(first)},,);
        Queue(first,tT,,,15,,,set-arg);
    };

    _1XX => {
        &record-crm(${STRFTIME(,,%G/%m/%d/)}${ID});
        &crm(${GROUP},${DSTNUM},${CIDNUM},${ID});
        Dial(SIP/${EXTEN});
    };

    h => {
        NoOP(${MEMBERINTERFACE});
        if ( ${EXISTS(${MEMBERINTERFACE})} ) {
            MSet(DSTNUM=${MEMBERINTERFACE:4});
            &crm-end(${GROUP},${DSTNUM},${CIDNUM},${ID});
            Hangup;
        };
        &crm-group-end(${QUEUE_MEMBER_LIST(first)},,);
    };
}

1000 — номер группы, тут будет вызываться очередь. В нашем примере очередь будет ограничена 3 агентами. При желании увеличить число агентов очереди, нужно добавить запятые после ${QUEUE_MEMBER_LIST(first)} и несколько строк в macro crm-group/crm-group-end. Запятые нужны для вызова макроса, т.е. нужно передавать столько аргументов, сколько ждет макрос, точнее передать можно и больше аргументов, но если об этом упомянуть в диалплане, то он ругнется и работать не будет. ${QUEUE_MEMBER_LIST(first)} Содержит агентов в очереди, в итоге получится, что мы передаем в макрос более 3 аргументов (агенты + два пустых аргумента), но это уже не проблема. Тут главное, чтобы агентов не было больше, чем принимаемых аргументов в макросе, иначе не все получат popup окно.

macro crm-group ( member1, member2, member3 ) {
    if (${EXISTS(${member1:4})) {
        MSet(DSTNUM=${member1:4}));
        &crm(${GROUP},${DSTNUM},${CIDNUM},${ID});
    };
    if (${EXISTS(${member2:4})) {
        MSet(DSTNUM=${member2:4});
        &crm(${GROUP},${DSTNUM},${CIDNUM},${ID});
    };
    if (${EXISTS(${member3:4})) {
        MSet(DSTNUM=${member3:4});
        &crm(${GROUP},${DSTNUM},${CIDNUM},${ID});
    };
    return;
}

Проверяем есть ли агент, назначаем его как принимающего вызов и отправляем запрос на вывод всплывающего окна.

После чего у нас уходит звонок в очередь first

Queue(first,tT,,,15,,,set-arg);

Тут главное не запутаться в запятых. В правилах хабра вроде написано, что не следует использовать смайлы/скобки, но тут я не удержался =)
Макрос set-arg вызываем командой Gosub, если бы макрос был записан в extensions.conf, то вызов писали бы на запятую раньше.

macro set-arg () {
    &crm-group-end(${QUEUE_MEMBER_LIST(first)},,);
    return;
}

Передать аргументы из команды Queue у меня не получилось, потому и пришлось прибегнуть к этому небольшому макросу.

macro crm-group-end ( member1, member2, member3 ) {

    if ( ${EXISTS(${member1:4}) ) {
        if (${member1:4}!=${CHANNEL(peername)}){
            MSet(DSTNUM=${member1:4});
            &crm-end(${GROUP},${DSTNUM},${CIDNUM},${ID});
        };
    };
    if ( ${EXISTS(${member2:4}) ) {
        if (${member2:4}!=${CHANNEL(peername)}){
            MSet(DSTNUM=${member2:4});
            &crm-end(${GROUP},${DSTNUM},${CIDNUM},${ID});
        };
    };
    if ( ${EXISTS(${member3:4}) ) {
        if (${member3:4}!=${CHANNEL(peername)}){
            MSet(DSTNUM=${member3:4});
            &crm-end(${GROUP},${DSTNUM},${CIDNUM},${ID});
        };
    };
    return;
}

Опять же, как и при запросе start, проверяем наличие агентов, сравниваем агента очереди с тем, кто снял трубку (${CHANNEL}) и, если нет совпадения, то отправляем stop (больше всплывающее окно нам не нужно).

Менеджер поговорил с клиентом, разговор закончен, и нам нужно об этом сообщить нашей CRM. Возвращаемся к контексту redirect

h => {
        if ( ${EXISTS(${MEMBERINTERFACE})} ) {
            MSet(DSTNUM=${MEMBERINTERFACE:4});
            &crm-end(${GROUP},${DSTNUM},${CIDNUM},${ID});
            Hangup;
        };
        &crm-group-end(${QUEUE_MEMBER_LIST(first)},,);
    };

Тут мы проверяем наличие ${MEMBERINTERFACE}, другими словами, был ли звонок обработан одним из агентов, если да, то отправляем в CRM stop для этого агента, если звонок сорвался, то отправляем stop для всех агентов очереди.
Осталось нам рассмотреть вариант с переводом звонка. Чтобы не терять клиентов при переводе, используем attended transfer (в features.conf я назначил для него #). Чтобы дополнить трансфер функцией отправки запросов в CRM, переназначим для него контекст. В моем случае это делается для всех вызовов, потому я просто в секции globals добавил TRANSFER_CONTEXT=transfer;

Перейдем к самому контексту:
context transfer {
    _1XX => {
        MSET(DSTNUM=${EXTEN});
        &crm(${GROUP},${DSTNUM},${CIDNUM},${ID});
        Dial(SIP/${DSTNUM});

    };
    h => {
        &crm-end(${GROUP},${DSTNUM},${CIDNUM},${ID});
    };
}

Получаем входящий вызов, жмем #100, в CRM уходит start c номером 100 (на кого переводим звонок) и номером клиента. Менеджер с номером 100 получает popup окно, его коллега может добавить пару слов о настроении клиента и положить трубку. Трансфер совершен. На этом этапе отправится запрос stop для вызова одного менеджера другому.

По завершению разговора менеджера с клиентом отправится два запроса stop — для первого и второго менеджера, ведь они оба поговорили с клиентом.

Автор: системный администратор компании Centos-admin.ru artzcom.
Tags:
Hubs:
+8
Comments 3
Comments Comments 3

Articles

Information

Website
slurm.io
Registered
Founded
Employees
51–100 employees
Location
Россия
Representative
Антон Скобин