📲 Липкость звонка

Использование IVR меню, в большинстве случаем, позволяет эффективно распределять входящие звонки и снижать нагрузку с секретаря. Клиент звонит, выбирает нужный отдел либо до набирает внутренний номер сотрудника и достигает цели. Но, как быть в ситуации, когда клиент не знает кому он звонит? Что если это наш менеджер не смог дозвонится клиенту, который перезвонил и попал на IVR? В данном случае будет полезным функционал «Липкости звонка». Мы уже рассматривали реализацию: Входящий звонок с маршрутизацией на ответственного в bpm’online. Сегодня мы рассмотрим пример маршрутизации в первую очередь на того, кто сегодня уже общался с данным номером либо звонил ему. Приступим!

Kibana нам в помощь

Для решения поставленной задачи нам понадобится приложение cdr, которое предназначено для поиска по журналу звонков в elasticsearch. В начале, нам нужно написать правильный запрос. Делаем тестовый звонок и открываем интерфейс Kibana, раздел Discover. Выбираем для отображения колонки extension (номер сотрудника), caller_id_number (номер абонента), destination (номер назначения).

Теперь сделаем запрос с фильтрацией по номеру абонента, он может быть либо в номере назначения (для исходящих) либо в номере абонента (для входящих):

destination_number:/0969716158/ OR caller_id_number:/0969716158/

Как результат, мы получаем номер сотрудника, extension, который общался либо звонил абоненту. И обязательно открываем Response (ответ со стороны elasticsearh), он нам пригодится ниже:

И так, как искать мы уже поняли, а теперь, добавим это все в маршрутизацию на стороне Webitel.

Запрос в CDR

Для работы с приложением cdr, нам нужно написать правильно запрос в elasticsearch

{
"limit": 1,
"sort": {
"created_time": {
"order": "desc",
"unmapped_type": "boolean"
}
},
"index": "cdr-a",
"query": "",
"columns": [
"extension"
],
"filter": [
{
"bool": {
"must": [
{
"range": {
"created_time": {
"gte": "now/d",
"lte": "now"
}
}
},
{
"query_string": {
"query": "destination_number:/.
${caller_id_number}/ OR caller_id_number:/.${caller_id_number}/",
"analyze_wildcard": true,
"default_field": "
"
}
}
]
}
}
]
}

Разберем запрос:

limit- Количество записей. 1 — так как нам нужно только последнего звонившего.

sort — Сортировку делаем от новых к старым, опять же — только последний.

index — В каком индексе осуществлять поиск: cdr-a

columns — Какие поля возвращать — нам нужен номер сотрудника: extension

query_string — Строка запроса в которой номер телефона заменен на канальную переменную caller_id_number

range — Временной интервал: начиная с 00:00 текущего дня до теперь.

А теперь посмотрим на ответ со стороны elasticsearh:

{
"took": 4,
"timed_out": false,
"_shards": {
"total": 10,
"successful": 10,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 5,
"max_score": null,
"hits": [
{
"_index": "cdr-a-2019-.bpmonline.com",
"_type": "cdr",
"_id": "bb643481-310a-45cb-8c4e-95244c8e02b8",
"_score": null,
"fields": {
"extension": [
"115"
]
},
"sort": [
1547032525216
]
}
]
}
}

Что бы добраться к нужному полю extension, мы должны пройти путь: hits => hits => fields => extension

Теперь давайте обновим нашу маршрутизацию в Public

Маршрутизация входящего звонка

У нас уже есть готовое IVR меню, так что мы добавим вначале проверку, и если результат не пустой, попытаемся соединить с нужным сотрудников (а с помощью userData добавим еще условие, что бы он был в статусе onhook — Готов). Если сотрудник не ответит в течение 15 секунд, тогда уже продолжим выполнять нашу типовую IVR схему:

["начало схемы"],

{
    "cdr": {
        "exportVar": {
            "last_cid": "hits.hits.0.fields.extension.0"
        },
        "elastic": {
            "limit": 1,
            "sort": {
                "created_time": {
                    "order": "desc",
                    "unmapped_type": "boolean"
                }
            },
            "index": "cdr-a",
            "query": "*",
            "columns": [
                "extension"
            ],
            "filter": [
                {
                    "bool": {
                        "must": [
                            {
                                "range": {
                                    "created_time": {
                                        "gte": "now/d",
                                        "lte": "now"
                                    }
                                }
                            },
                            {
                                "query_string": {
                                    "query": "caller_id_number:/.*${caller_id_number}/ AND destination_number:${destination_number}",
                                    "analyze_wildcard": true,
                                    "default_field": "*"
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }
},
{
    "if": {
        "expression": "${last_cid}",
        "then": [
            {
                "userData": {
                    "name": "${last_cid}",
                    "var": "account_state",
                    "setVar": "acc_state"
                }
            },
            {
                "log": "CID: ${caller_id_number}, Ext: ${last_cid} (${acc_state})"
            },
            {
                "if": {
                    "expression": "${last_cid} && ${acc_state} == 'onhook'",
                    "then": [
                        {
                           "bridge": {
                               "endpoints": [
                                     {
                                       "name": "${last_cid}",
                                       "type": "user",
                                       "parameters": [
                                          "leg_timeout=15"
                                       ]
                                    }
                                 ]
                             }
                        }
                    ]
                }
            }
        ]
    }
},

["продолжение схемы"]

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.