События (логин, логаут) агентов отслеживаются ресурсом События агента очереди (/agent/.../event/)
Основные события (ивенты) звонков - это DIAL-IN, DIAL-OUT, ANSWER и HANGUP.
По каждому событию типа DIAL-IN, DIAL-OUT, ANSWER удобно создавать/обновлять отдельную структуру данных типа:
<Идентификационный ключ звонка>: {<данные звонка>}
Пример:
calls: { 129509OUTnri4l2sngj9k3lsm5ndkfs0: { "CallerExtension": "0004*999@sip.ringme.ru", "CallerExtensionID": 129509, "CalledExtension": "0004*315@sip.ringme.ru", "CalledExtensionID": 438108, "EventType": "answer", "CallID": "nri4l2sngj9k3lsm5ndkfs0", "CallerIDNum": "+79127695081", "CallerIDName": "+79127695081", "CalledDID": "74956637373", "CallStatus": "ANSWER", "CallFlow": "out", "CalledNumber": "0004*315", "SubCallID": "129409-nri4l2sngj9k3lsm5ndkfs0", "CallAPIID": "167903702-nri4l2sngj9k3lsm5ndkfs0", "EventTime": 16884456543096 }, 387295INni548n294mndfinu5skjn56e: {...etc}, ...etc }
А по событию HANGUP (а в некоторых случаях и по ANSWER) удалять эти структуры данных по идентификационным ключам
Идентификационный ключ соединяет в себе три сущности: ExtensionId, CallFlow и CallId. Рекомендации по формированию идентификационного ключа для каждого типа события указаны ниже в каждом типе событий.
Если в вашем интерфейсе не планируется отображение какого-либо состояния звонка, то вы можете просто игнорировать ненужные события. Например, в вашем интерфейсе отображаются только отвеченные звонки (в состоянии “answered”). Значит, вам нужно заводить новые структуры данных только по событиям answer, а dial-in и dial-out можно просто проигнорировать.
События можно анализировать следующим образом:
EventType “dial-in”
Обозначает входящий на добавочный. В таком звонке нас интересует, КОМУ звонят (кто calledExtension)
Идентификационный ключ такого звонка: CalledExtensionId + "IN" + CallId
ЕСЛИ calledExtension - это очередь, значит звонящий попал в очередь и ждет, когда ему ответит кто-либо из агентов этой очереди.
Можно отобразить в интерфейсе: входящий дозвон на очередь ("висящий в очереди" звонок) *
ЕСЛИ calledExtension - это phone, значит сейчас на этот phone кто-то пытается дозвониться
Можно отобразить в интерфейсе: входящий дозвон на конкретного сотрудника
EventType “dial-out”
Обозначает исходящий вызов с добавочного. В этом звонке нас интересует, КТО звонит (кто callerExtension)
Идентификационный ключ такого звонка: CallerExtensionId + "OUT" + CallId
ЕСЛИ callerExtension - это очередь, значит очередь сейчас звонит кому-то из своих агентов
Можно отобразить: исходящий дозвон от очереди на конкретного сотрудника
ЕСЛИ callerExtension - это phone, значит phone сейчас пытается кому-то дозвониться
Можно отобразить: исходящий дозвон от конкретного сотрудника на какой-либо номер
EventType “answer”
Обозначает ответ на звонок. Здесь нам нужно анализировать направление звонка (callFlow)
CallFlow “in”
Это ответ на входящий звонок. Нас интересует КТО ответил (calledExtension)
Идентификационный ключ такого звонка: CalledExtensionId + "IN" + CallId
ЕСЛИ calledExtension - это очередь, то мы игнорируем такое событие, так как попадание звонка в очередь уже создается по событию dial-in на очередь
ЕСЛИ calledExtension - это phone, значит кто-то из сотрудников ответил на звонок.
Можно отобразить: ответивший сотрудник разговаривает прямо сейчас (ответив на входящий звонок)
CallFlow “out”
Это ответ на исходящий звонок. Нас интересует КОМУ ответили (callerExtension)
Идентификационный ключ такого звонка: CallerExtensionId + "OUT" + CallId
ЕСЛИ callerExtension - это очередь, значит агент очереди ответил на звонок.
Можно отобразить: удаление звонка из списка "висящих в очереди" (если вы его создавали на этапе, который описан выше - EventType “dial-in” calledExtension очередь - помечено звездочкой (*) )
ЕСЛИ callerExtension - это phone, значит позвонившему сотруднику кто-то ответил.
Можно отобразить: позвонивший сотрудник разговаривает прямо сейчас (получил ответ, совершив исходящий вызов)
EventType “hangup”
Обозначает окончание звонка. Нам как и ранее нужно анализировать направление звонка (CallFlow)
CallFlow “in”
Это хэнгап на входящий звонок. Нас интересует КОМУ звонили в этом звонке (calledExtension). Здесь нам нужно завершить звонок по такому ключу: CalledExtensionId + "IN" + CallId
CallFlow “out”
Это хэнгап на исходящий звонок. Нас интересует КТО звонил в этом звонке (callerExtension). Здесь нам нужно завершить звонок по такому ключу: CallerExtensionId + "OUT" + CallId
Разберем возможный пример:
У вас есть очередь 200 (extensionId 222222) с одним агентом 101 (extensionId 111111). В очередь поступает звонок с номера +798198736545.
Вам поступает ивент DIAL-IN, в котором CalledExtension - это очередь. CallId этого звонка qqqwwweeerrrtttyyy999. Вы формируете идентификационный ключ: 222222INqqqwwweeerrrtttyyy999 и кладете информацию о событии по этому ключу в структуру данных:
calls: {
222222INqqqwwweeerrrtttyyy999: {<данные события>} // 1
}
С помощью этой информации вы отображаете “висящий в очереди звонок”
Очередь начинает обзванивать своих агентов. Вам поступает событие DIAL-OUT, в котором CallerExtension - это очередь. Вы формируете новый ключ и добавляете данные этого события в структуру данных:
calls: {
222222INqqqwwweeerrrtttyyy999: {<данные события>}, // 1
222222OUTqqqwwweeerrrtttyyy999: {<данные события>} // 2
}
С помощью этой информации вы отображаете, кому сейчас звонит очередь
Так как в очереди только один агент 101, очередь сразу начнет дозвон на него и вам придет событие DIAL-IN, в котором агент 101 будет CalledExtension. Вы формируете новый ключ и добавляете данные этого события в структуру данных:
calls: {
222222INqqqwwweeerrrtttyyy999: {<данные события>}, // 1
222222OUTqqqwwweeerrrtttyyy999: {<данные события>}, // 2
111111INqqqwwweeerrrtttyyy999: {<данные события>} // 3
}
С помощью этих данных вы отображаете, что у агента 101 сейчас есть входящий звонок, но он еще не снял трубку
Когда 101 поднимет трубку, вам придет два события ANSWER.
Первый ANSWER c CallFlow === “in”, в котором CalledExtension будет агент 101. Это событие сигнализирует, что 101 ответил на входящий звонок.
Идентификационный ключ для этого события будет такой же, как и на событие DIAL-IN на 101 (111111INqqqwwweeerrrtttyyy999**)**. Когда мы положим в структуру данных новое событие по этому ключу, оно заменит старое событие. И это правильно, так как теперь агент 101 находится в разговаривающем состоянии, а не в состоянии вызова. Вот так будет выглядеть обновленная структура данных:
calls: {
222222INqqqwwweeerrrtttyyy999: {<данные события>}, // 1
222222OUTqqqwwweeerrrtttyyy999: {<данные события>}, // 2
111111INqqqwwweeerrrtttyyy999: {<данные нового события>} // 3
}
Теперь мы по этим данным отображаем не дозвон очереди на 101, а то, что 101 уже разговаривает.
Помимо этого, так как мы уже получили ответ от добавочного, нам не нужно отображать “висящий в очереди” звонок. Так что удаляем входящий в очередь звонок (под номером 1) из структуры данных:
calls: {
222222OUTqqqwwweeerrrtttyyy999: {<данные события>}, // 2
111111INqqqwwweeerrrtttyyy999: {<данные нового события>} // 3
}
Второй ANSWER мы получаем с CallFlow === “out”, и в нем CallerExtension - очередь. Это событие сигнализирует, что кто-то ответил на дозвон от очереди. При формировании идентификационного ключа мы так же обнаружим, что он совпадает с уже существующим ключом (222222OUTqqqwwweeerrrtttyyy999). Нам не нужно ничего отображать по этому событию, так что просто удалим его по этому ключу.
Вот так будет выглядеть структура после этого:
calls: {
111111INqqqwwweeerrrtttyyy999: {<данные нового события>} // 3
}
На данный момент у нас отображается только текущий разговор добавочного 101.
Когда он закончит разговаривать, начнут прилетать события типа HANGUP. Два хэнгапа прилетят на те звонки, которые мы уже ранее завершили по событию ANSWER, поэтому с ними делать ничего не надо. И один из хэнгапов будет иметь CallFlow === “in” и в качестве CalledExtension будет иметь агента 101. Идентификационный ключ такого события будет совпадать с ключом последнего оставшегося в структуре данных события (под номером 3). По этому ключу мы его удаляем:
calls: {}
На этом у нас завершились все звонки и в интерфейсе больше ничего не отображается.
NB: Данный документ - это упрощенный пример построения системы анализа ивентов. Их можно анализировать и другими способами и создавать структуры данных по-другому. Подберите вариант, подходящий для конкретно вашей интеграции.