Глава 10

Очереди автоматического распределения вызовов (ACD)

Англичанин, даже если он один,

формирует упорядоченную очередь из одного.

-George Mikes

Автоматическое распределение вызовов (Automatic Call Distribution — ACD, а по-русски АРВ) или организация очередности вызовов предоставляет УАТС возможность помещать в очередь входящие вызовы от группы пользователей: она объединяет несколько вызовов в шаблон удержания, назначает каждому вызову ранг и определяет порядок в котором этот вызов должен быть доставлен доступному агенту (как правило, с начала списка). Когда агент становится доступным, вызывающий абонент с наивысшим рангом в очереди доставляется этому агенту, а все остальные перемещаются вверх по рангу.

Если вы когда-либо звонили в организацию и слышали «Все наши операторы заняты», вы испытали АРВ. Преимуществом АРВ для вызывающих абонентов является то, что им не нужно повторять набор номера, чтобы попытаться связаться с кем-то, а преимущества для организаций заключаются в том, что они могут лучше обслуживать своих клиентов и временно обрабатывать ситуации, когда абонентов больше, чем агентов.1

Существует два типа центров обработки вызовов: входящий и исходящий. АРВ относится к технологии, которая обрабатывает входящие вызовы, тогда как термин Dialer (или Predictive Dialer) относится к технологии, которая обрабатывает исходящие вызовы. В этой книге мы будем в основном сосредотачиваться на входящем вызове.

Все мы были разочарованы плохо спроектированными и управляемыми очередями: длительная музыка на удержание с радио, которое не настроено, ошеломляющее время ожидания и бессмысленные сообщения, которые каждые 20 секунд повторяют, насколько важен ваш звонок, несмотря на тот факт, что вы ждали 30 минут и слышали сообщение столько раз, что можете процитировать его по памяти. С точки зрения обслуживания клиентов проектирование очереди может быть одним из наиболее важных аспектов вашей телефонной системы. Как и в случае с автосекретарем, следует иметь в виду, прежде всего, то, что ваши абоненты не заинтересованы в удержании в очереди. Они позвонили, потому что они хотят поговорить с тобой. Все ваши проектные решения должны держать этот важный факт в центре внимания: люди хотят поговорить с другими людьми, а не с вашей телефонной системой.2

Цель этой главы — научить вас создавать и проектировать очереди, которые доставят абонентов к намеченным местам назначения как можно быстрее и безболезненнее.

В этой главе мы можем переключаться между использованием терминов участники очереди3 и агенты. Поскольку мы не будем тратить много времени на модуль Asterisk с именем chan_agent (используя AgentLogin()), мы должны четко указать, что в этой книге, когда мы используем термин «агент», мы имеем в виду конечную точку — человека, а не технологию канала в Asterisk с именем chan_agent. Читайте дальше, и тогда узнаете больше.

Создание простой очереди ACD

Для начала мы создадим простую очередь ACD. Она будет принимать абонентов и пытаться доставить их участнику очереди.

В Asterisk термин участник относится к каналу (как правило, SIP-пир), назначенному очереди, которому можно набрать, например SIP/0000FFFF0001. Агент технически относится к каналу Agent, также используемому для набора конечных точек. К сожалению, канал Agent является устаревшей технологией в Asterisk, поскольку он ограничен гибкостью и может вызвать неожиданные проблемы, которые трудно диагностировать и решить. Мы не будем использовать chan_agent, поэтому имейте в виду, что мы обычно будем использовать термин участник для ссылки на телефонное устройство и агент для обозначения человека, который обрабатывает вызов. Поскольку один из них обычно не действует без другого, любой термин может относиться к обоим.

Мы создадим очереди в файле queues.conf и вручную добавим участников очереди через консоль Asterisk. В разделе «Участники очереди» мы рассмотрим, как создать диалплан, который позволяет нам динамически добавлять и удалять участников очереди (а также приостанавливать и возобновлять их).

Первым шагом является создание пустого файла agent.conf в каталоге конфигурации /etc/asterisk. Мы не будем использовать или редактировать этот файл, однако модуль app_queue ожидает его найти и не будет загружаться, если он не существует:

$ cd /etc/asterisk

$ touch agents.conf

Затем вам нужно создать queues.conf, в котором определена конфигурация для реальных очередей:

$ touch queues.conf

Заполните ее следующей конфигурацией, которая создаст две очереди с именами [sales] и [support]. Вы можете назвать их как захотите, но мы будем использовать в книге эти имена и далее, поэтому, если вы используете другие имена очередей, отличные от тех что мы здесь рекомендовали, обратите внимание на свой выбор для использования в будущем:

[general]

autofill=yes ; распределять всех ожидающих абонентов доступным

; участникам

shared_lastcall=yes ; соблюдать время отдыха для участников,

; зарегистрированных в более чем одной очереди

[StandardQueue](!) ; шаблон для предоставления общих функций

musicclass=default ; воспроизводить музыку [default]

strategy=rrmemory ; использование стратегии Round Robin Memory

joinempty=no ; не присоединяться к очереди, если нет доступных

; участников

leavewhenempty=yes ; покинуть очередь, когда нет доступных

; участников

ringinuse=no ; не звонить участникам, когда они уже заняты

; (предотвращает множественные вызовы агенту)

[sales](StandardQueue) ; создать очередь sales с использованием параметров

; шаблона StandardQueue

[support](StandardQueue) ; создать очередь support с использованием

; параметров шаблона StandardQueue

Раздел [general] определяет поведение по умолчанию и глобальные параметры. Мы указали только два варианта в разделе [general], так как для для наших нужд на данный момент достаточно встроенных значений по умолчанию.

Первый параметр — autofill, который указывает очереди распределять всех ожидающих абонентов всем доступным участникам немедленно. Предыдущие версии Asterisk распределяли только одного вызывающего абонента за раз — это означало, что, хотя Asterisk сигнализировал агенту, все остальные вызовы удерживались (даже если были доступны другие агенты) до тех пор, пока первый вызывающий абонент не был подключен к агенту (что, очевидно, приводило к узким местам в старых версиях Asterisk, где использовались большие очереди). Если у вас нет особой необходимости обратной совместимости, этот параметр всегда должен быть установлен в значение yes.

Второй вариант в секции [general] queues.conf является shared_lastcall. Когда мы включаем shared_lastcall, последний вызов агента, который зарегистрирован в нескольких очередях, будет вызовом, который подсчитывается для времени отдыха4, чтобы избежать отправки вызова агенту из другой очереди в это время. Если для этой опции установлено значение no, время отдыха будет применяться только к очереди, из которой пришел последний вызов, что означает — агент, который завершает вызов из очереди support, все равно может получить вызов из очереди sales. Эта опция также должна всегда быть установлена в yes (по умолчанию).

Следующий раздел [StandardQueue](!) — это шаблон, который мы будем применять к нашим очередям sales и support (мы объявили его шаблоном, добавив (!)). Мы определили musicclass как default в режиме ожидания, как это было настроено в файле musiconhold.conf. strategy мы будем использовать rrmemory, что означает Round-Robin with Memory (циклический перебор с памятью). Стратегия rrmemory работает путем ротации через агентов в очереди в последовательном порядке, отслеживая какой агент получил последний вызов, и представляя следующий вызов следующему агенту. Когда она добирается до последнего агента, то возвращается наверх (когда агенты входят в систему, они добавляются в конец списка). Мы установили joinempty в no, поскольку это вообще дурной тон помещать вызывающих абонентов в очередь, где нет агентов, готовых принять их звонки.

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

Опция leavewhenempty используется для управления тем, должны ли вызывающие абоненты выпадать из приложения Queue() в диалплан если нет доступных участников для принятия их вызовов. Мы установили значение yes, так как абоненты не должны стоять в очереди без зарегистрированных агентов.

С точки зрения бизнеса вы должны сообщать своим агентам чтобы они очищали все вызовы из очереди перед выходом из системы в течение дня. Если вы обнаружите, что в конце дня в очереди много вызовов, то можете рассмотреть вопрос о продлении чьей-то смены чтобы справиться с ними. В противном случае они просто добавят вам стресса, когда вернутся на следующий день в плохом настроении.

Вы можете использовать GotoIfTime() ближе к концу дня, чтобы перенаправить абонентов на голосовую почту или другое подходящее место в вашем диалплане, в то время как ваши агенты очищают все оставшиеся вызовы в очереди.

Наконец, мы настроили ringinuse на no, что говорит Asterisk не звонить участникам когда их устройства уже звонят. Цель установки ringinuse в no состоит в том, чтобы избежать нескольких вызовов одного и того же участника из разных очередей.

Следует отметить, что, joinempty и leavewhenempty ищут либо отсутствие участников в очереди, либо их недоступность. Агенты, которые являются Ringing или InUse не считаются недоступными, поэтому не будут блокировать звонящих от присоединения к очереди или выкидывать их при joinempty=no и/или leavewhenempty=yes.

После того, как вы закончили настройку в вашем файле queues.conf, вы должны сохранить его и перезагрузить модуль app_queue.so из своего Asterisk CLI:

$ asterisk -r

* CLI> module reload app_queue.so

— Reloading module ‘app_queue.so’ (True Call Queuing)

Затем убедитесь что ваши очереди были загружены в память (не забудьте обеспечить наличие пустого файла agents.conf):

localhost*CLI> queue show

support has 0 calls (max unlimited) in ‘rrmemory’ strategy

(0s holdtime, 0s talktime), W:0, C:0, A:0, SL:0.0% within 0s

No Members

No Callers

sales has 0 calls (max unlimited) in ‘rrmemory’ strategy

(0s holdtime, 0s talktime), W:0, C:0, A:0, SL:0.0% within 0s

No Members

No Callers

Вывод queue show предоставляет различные фрагменты информации, включая те детали, которые приведены в таблице 13-1.

Таблица 13-1. Вывод команды CLI queue show

Поле Описание
W: Вес очереди
C: Количество вызовов, направленных в очередь
A: Количество вызовов, отвеченных участниками
SL: Уровень обслуживания

Теперь, когда вы создали очереди, вам нужно настроить ваш диалплан, чтобы разрешить вызовам входить в очередь.

Добавьте следующую логику в файл диалплана extensions.conf:

[Queues]

exten => 7001,1,Verbose(2,${CALLERID(all)} вход в очередь support)

same => n,Queue(support)

same => n,Hangup()

exten => 7002,1,Verbose(2,${CALLERID(all)} вход в очередь sales)

same => n,Queue(sales)

same => n,Hangup()

[LocalSets]

include = > Queues ; разрешить телефонам вызов очередей

Мы включили Queues в контекст LocalSets чтобы наши телефоны могли вызывать очереди, которые мы настроили. В Главе 15 мы определим пункты меню, которые ведут в эти очереди. Сохраните изменения в файле extensions.conf и перезагрузите диалплан с помощью команды CLI dialplan reload.

Если вы наберете номер 7001 или 7002 на этом этапе, то получите следующий результат:

— Executing [7001@LocalSets:1] Verbose(«SIP/0000FFFF0003-00000001»,

«2,»Leif Madsen» <100> entering the support queue») in new stack

== «Leif Madsen» <1—> entering the support queue

— Executing [7001@LocalSets:2] Queue(«SIP/0000FFFF0003-00000001»,

«support») in new stack

[2011-02-14 08:59:39] WARNING[13981]: app_queue.c:5738 queue_exec:

Unable to join queue ‘support’

— Executing [7001@LocalSets:3]

Hangup(«SIP/0000FFFF0003-00000001», «») in new stack

== Spawn extension (LocalSets, 7001, 3) exited non-zero on

‘SIP/0000FFFF0003-00000001

Вы не присоединитесь к очереди на этом этапе, так как в очереди нет агентов для ответа на вызовы. Поскольку у нас есть joinempty=no и leavewhenempty=yes, настроенные в queues.conf, абоненты не будут помещены в очередь. (Это было бы хорошей возможностью для экспериментов с вариантами joinempty и leavewhenempty в queues.conf, чтобы лучше понять их влияние на очереди.)

В следующем разделе мы продемонстрируем, как добавлять участников в очередь (а также другие взаимодействия участников с очередью, такие как пауза/возобновление).

Участники очереди

Очереди не очень полезны, если кто-то не отвечает на входящие в них вызовы, поэтому нам нужен метод, позволяющий агентам регистрироваться в очереди для ответа на вызовы. Существуют различные способы для этого, поэтому мы покажем вам как добавлять участников в очередь как вручную (как администратор либо через CLI, либо жестко в файле queues.conf), так и динамически (в качестве агента через расширение определенное в диалплане). Мы начнем с метода CLI Asterisk, который позволит вам легко добавлять участников в очередь для тестирования с минимальными изменениями диалплана. Далее мы покажем как можно определить элементы в queues.conf (которые будут добавлять определенных участников когда app_queue перезагружается). Наконец, мы покажем вам, как добавить логику диалплану, которая позволит агентам входить в очереди и выходить из них, а также приостанавливать и возобновлять себя в очередях, в которые они вошли.

Управление участниками очереди с помощью CLI

Мы можем добавить участников очереди в любую доступную очередь через команду Asterisk CLI queue add. Формат команды queue add (все в одной строке):

* CLI> queue add member <channel> to <queue> [[[penalty <penalty>] as

<membername>] state_interface <interface>]

Элемент <channel> является каналом, который мы хотим добавить в очередь, например SIP/0000FFFF0003, а имя <queue> будет чем-то вроде support или sales — любое имя очереди, которое существует в /etc/asterisk/queues.conf. Пока мы будем игнорировать <penalty>, но обсудим это в «Расширенных очередях» (penalty используется для управления рангом участника в очереди, что может быть важным для агентов, которые вошли в несколько очередей или имеют разные навыки). Мы можем определить <membername> чтобы предоставить подробные сведения механизму логирования очередей.

Опция state_interface — это то, что нам нужно рассмотреть более внимательно. Поскольку это так важно для всех аспектов очередей и их участников в Asterisk мы написали небольшой раздел об этом, так что прочитайте «Введение в состояния устройств«. Как только вы это настроите, вернитесь сюда и продолжайте. Не волнуйтесь, мы подождем.

Теперь, когда вы добавили callcounter=yes в sip.conf (мы будем использовать SIP-каналы во всех остальных наших примерах), давайте посмотрим как добавить участников в наши очереди из Asterisk CLI.

Добавление участника очереди support может быть выполнено командой queue add member:

* CLI> queue add member SIP/0000FFFF0001 to support

Added interface ‘SIP/0000FFFF0001’ to queue ‘support’

Запрос очереди проверит, добавлен ли наш новый участник:

CLI> queue show support

support has 0 calls (max unlimited) in ‘rrmemory’ strategy

(0s holdtime, 0s talktime), W: 0, C: 0, A: 0, SL: 0.0% внутри 0s

Members:

SIP/0000FFFF0001 (dynamic) (Not in Use) has taken no calls yet

No callers

Чтобы удалить участника очереди, вы должны использовать команду queue remove member:

* CLI> queue remove member SIP/0000FFFF0001 from support

Removed interface ‘SIP/0000FFFF0001’ from queue ‘support’

Конечно, вы можете ещё раз использовать команду queue show чтобы убедиться, что ваш участник удален из очереди.

Мы также можем приостанавливать и возобновлять участников в очереди из консоли Asterisk командами queue pause member и queue unpause member. Они принимают аналогичный формат с предыдущими командами, которые мы использовали:

*CLI> queue pause member SIP/0000FFFF0001 queue support reason DoingCallbacks

paused interface ‘SIP/0000FFFF0001’ in queue ‘support’ for reason ‘DoingCallBacks’

*CLI> queue show support

support has 0 calls (max unlimited) in ‘rrmemory’ strategy

(0s holdtime, 0s talktime), W:0, C:0, A:0, SL:0.0% within 0s

Members:

SIP/0000FFFF0001 (dynamic) (paused) (Not in use) has taken no calls yet

No Callers

Добавив причину приостановки участника очереди, например lunchtime (время обеда) можно гарантировать, что журналы очереди будут содержать некоторые дополнительные сведения, которые могут быть полезны. Вот как вернуть участника:

*CLI> queue unpause member SIP/0000FFFF0001 queue support reason off-break

unpaused interface ‘SIP/0000FFFF0001’ in queue ‘support’ for reason ‘off-break’

*CLI> queue show support

support has 0 calls (max unlimited) in ‘rrmemory’ strategy

(0s holdtime, 0s talktime), W:0, C:0, A:0, SL:0.0% within 0s

Members:

SIP/0000FFFF0001 (dynamic) (Not in use) has taken no calls yet

No Callers

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

Определение участников очереди в файле queues.conf

Если вы определяете участника очереди в файле queues.conf, то этот участник всегда будет регистрироваться в очереди, когда она перезагружается. Как правило, вы будете использовать этот метод только в том случае, если хотите чтобы участник всегда регистрировался (что обычно не очень хорошо если ваши участники являются людьми, поскольку люди склонны вставать и двигаться).

В каждом определении очереди вы просто определяете элементы таким образом:

[general]

; все общие настройки, которые мы обсуждали до

[sales](StandardQueue)

member => SIP/0000FFFF0005 ; или любой другой канал

[service](StandardQueue)

member => SIP/0000FFFF0006

В обычной очереди (в которой у вас есть группа людей, отвечающих на вызовы), вы обнаружите, что определение участников в файле queues.conf не будет служить хорошей идеей. Агенты должны иметь возможность входа и выхода (и не должны автоматически регистрироваться каждый раз, когда очередь перезагружается). Мы не рекомендуем определять участников в файле queues.conf, если у них нет другой цели (например, банк устройств, которые отвечают на вызовы, где вы хотите использовать очередь для балансировки нагрузки в пул устройств или группу вызовов, где все телефоны отвечают на все вызовы постоянно).

Управления участниками очереди с помощью логики диалплана

В колл-центре, в котором работают живые агенты, наиболее часто агенты сами входят и выходят из системы в начале и конце их смены (или когда они отправляются на обед, в ванную комнату, или иным образом недоступны для очереди).

Чтобы включить это мы будем использовать следующие приложения диалплана:

  • AddQueueMember()
  • RemoveQueueMember()

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

  • PauseQueueMember()
  • UnpauseQueueMember()

Может быть проще подумать об этих приложениях следующим образом: приложения для добавления и удаления используются для входа и выхода из системы, а пара пауза/возобновление используется для коротких периодов недоступности. Разница просто в том, что пауза/возобновление задает участника как недоступного/доступного фактически не удаляя его из очереди. Это полезно для целей отчетности (если участник приостановлен диспетчер очереди может видеть что он зарегистрирован в очереди, но просто недоступен для вызова в данный момент). Если вы не знаете какой из них использовать мы рекомендуем, чтобы агенты использовали добавление/удаление всякий раз, когда они физически не находятся на своем телефоне и паузу/возобновление, когда они находятся за столом, но временно недоступны.

Использование Pause и Unpause

Использование паузы и возобновления является вопросом предпочтения. В некоторых средах эти параметры могут использоваться для всех видов деятельности в течение дня, которые делают агента недоступным (например, во время обеденного перерыва и при выполнении работы, не связанной с очередью). Однако в большинстве колл-центров, если агент не находится рядом с телефоном и готов принять вызов в тот момент, он не должен быть зарегистрирован вообще, даже если он отлучается только на несколько минут от своего стола (например, для перерыва в туалет).

Некоторым супервизорам нравится использовать настройки добавления/удаления и паузы/возобновления в качестве своего рода часов учёта чтобы они могли отслеживать когда их сотрудники прибудут на работу и уйдут в конце дня и сколько времени они проводят за своими столами и на перерывах. Мы не считаем это хорошей практикой, поскольку целью этих приложений является информирование очереди о доступности агента, а не возможностью отслеживания действий сотрудников.

Важная вещь, которую следует здесь отметить, относится к параметру joinempty в queues.conf, который обсуждался ранее. Если агент приостановлен — он считается вошедшим в очередь. Скажем, ближе к концу дня один агент поставил себя на паузу несколько часов назад, чтобы работать над проектом. Все остальные агенты вышли из системы и ушли домой. Поступает вызов. В очереди указывается, что агент в очереди и поэтому будет стоять в очереди на вызов, хотя реальность такова, что в настоящее время нет людей, которые обслуживают эту очередь. Этот вызывающий абонент может оставаться в очереди без персонала неограниченно долго.

Короче говоря, агенты, которые не сидят за своими столами и не планируют быть доступными для звонков в течение следующих нескольких минут, должны выйти из системы. Пауза/возобновление должны использоваться только для кратковременных недоступностей (или вообще). Если вы хотите использовать свою телефонную систему в качестве часов учёта, есть много отличных способов сделать это с помощью Asterisk, но мы не рекомендуем использовать для этого приложения участника очереди.

Давайте построим простую логику диалплана, которая позволит нашим агентам указывать свою доступность для очереди. Мы собираемся использовать функцию диалплана CUT() для извлечения имени нашего канала из нашего вызова в систему, чтобы очередь узнала какой канал входит в очередь.

Мы создали этот диалплан, чтобы показать простой процесс входа и выхода из очереди, а также изменение приостановленного состояния участника в очереди. Мы делаем это только для одной очереди, которую предварительно определили в файле queues.conf. Статус переменных канала AddQueueMember(), RemoveQueueMember(), PauseQueueMember() и UnpauseQueueMember() приложений может быть использован приложением Playback() для объявления участникам очереди после того, как они выполнили определенные функции, чтобы дать им знать об успешности входа/выхода или паузы/возобновленния):

[QueueMemberFunctions]

exten => *54,1,Verbose(2,Logging In Queue Member)

same => n,Set(MemberChannel=${CHANNEL(channeltype)}/${CHANNEL(peername)})

same => n,AddQueueMember(support,${MemberChannel})

same => n,Verbose(1,${AQMSTATUS}) ; ADDED, MEMBERALREADY, NOSUCHQUEUE

same => n,Playback(agent-loginok)

same => n,Hangup()

exten => *56,1,Verbose(2,Logging Out Queue Member)

same => n,Set(MemberChannel=${CHANNEL(channeltype)}/${CHANNEL(peername)})

same => n,RemoveQueueMember(support,${MemberChannel})

same => n,Verbose(1,${RQMSTATUS}; REMOVED, NOTINQUEUE, NOSUCHQUEUE

same => n,Playback(agent-loggedoff)

same => n,Hangup()

exten => *72,1,Verbose(2,Pause Queue Member)

same => n,Set(MemberChannel=${CHANNEL(channeltype)}/${CHANNEL(peername)})

same => n,PauseQueueMember(support,${MemberChannel})

same => n,Verbose(1,${PQMSTATUS}); PAUSED, NOTFOUND

same => n,Playback(dictate/paused)

same => n,Hangup()

exten => *87,1,Verbose(2,Unpause Queue Member)

same => n,Set(MemberChannel=${CHANNEL(channeltype)}/${CHANNEL(peername)})

same => n,UnpauseQueueMember(support,${MemberChannel})

same => n,Verbose(1,${UPQMSTATUS}); UNPAUSED, NOTFOUND

same => n,Playback(agent-loginok)

same => n,Hangup()

Автоматическая регистрация в нескольких очередях

Обычно агент является участником более чем одной очереди. Вместо того, чтобы иметь отдельный номер для входа в каждую очередь (или требовать информацию от агентов о том, в какие очереди они хотят войти), этот код использует базу данных Asterisk (astdb) для хранения информации об участии в очереди для каждого агента, и затем перебирает каждую очередь, в которую входят агенты, регистрируя их по очереди.

Чтобы этот код работал, запись должна быть добавлена в AstDB через Asterisk CLI. Например, следующее будет хранить участника 0000FFFF0001 в обеих очередях — как в support, так и в sales:5

*CLI> database put queue_agent 0000FFFF0001/available_queues support^sales

Вам нужно будет сделать это один раз для каждого агента, независимо от количества очередей, в которые они входят.

Если вы затем запросите базу данных Asterisk, то получите результат, похожий на следующий:

pbx*CLI> database show queue_agent

/queue_agent/0000FFFF0001/available_queues : support^sales

Следующий код диалплана — пример того, как разрешить этому участнику очереди автоматически добавляться как в очередь support, так и в sales. Мы определили подпрограмму, которая используется для настройки трех канальных переменных (MemberChannel, MemberChanType, AvailableQueues). Эти переменные канала затем используются для входа (*54), выхода из системы (*56), паузы (*72) и возобновления (*87). Каждое из расширений использует подпрограмму subSetupAvailableQueues для установки этих переменных канала и для проверки того, что AstDB содержит список одной или нескольких очередей для устройства, как участника очереди:

[subSetupAvailableQueues]

;

; Эта подпрограмма используется различными подпрограммами login/logout/

; pausing/unpausing в контексте [ACD]. Цель подпрограммы-упростить

; централизацию поиска информации.

;

exten => start,1,Verbose(2,Checking for available queues)

; Получение текущего имени канала пира (0000FFFF0001)

same => n,Set(MemberChannel=${CHANNEL(peername)})

; Получение текущей технологии канала (SIP, IAX, etc)

same => n,Set(MemberChanType=${CHANNEL(channeltype)})

; Получение списка очередей, доступных этому агенту

same => n,Set(AvailableQueues=${DB(queue_agent/${MemberChannel}/

available_queues)})

; *** Это все должно быть в одной строке

; если нет очередей, назначенных для этого агента, мы будем обрабатывать его

; в расширении no_queues_available

same => n,GotoIf($[${ISNULL(${AvailableQueues})}]?no_queues_available,1)

same => n,Return()

exten => no_queues_available,1,Verbose(2,No queues available for agent

${MemberChannel})

; *** Это все должно быть в одной строке

; воспроизведение сообщения о том, что канал еще не назначен

same => n,Playback(silence/1&channel&not-yet-assigned)

same => n,Hangup()

[ACD]

;

; Используется для входа агентов во все настроенные очереди согласно AstDB

;

;

; Вход в несколько очередей через систему AstDB

exten => *54,1,Verbose(2,Logging into multiple queues per the database values)

; получить доступные очереди для этого канала

same => n,GoSub(subSetupAvailableQueues,start,1())

same => n,Set(QueueCounter=1) ; установка переменной счетчика

; Используя CUT(), получить первую очередь, возвращенную из AstDB

; Обратите внимание, что мы использовали ‘^’ в качестве разделителя

same => n,Set(WorkingQueue=${CUT(AvailableQueues,^,${QueueCounter})})

; Пока переменная канала WorkingQueue содержит значение — выполнять цикл

same => n,While($[${EXISTS(${WorkingQueue})}])

; AddQueueMember(queuename[,interface[,penalty[,options[,membername

; [,stateinterface]]]]])

; Добавляем канал в очередь, установив интерфейс для вызова и интерфейс для

; мониторинга состояния устройства

;

; *** Это все должно быть в одной строке

same => n,AddQueueMember(${WorkingQueue},${MemberChanType}/

${MemberChannel},,,${MemberChanType}/${MemberChannel})

same => n,Set(QueueCounter=$[${QueueCounter} + 1]) ; увеличиваем наш

; счетчик

; получаем следующую доступную очередь; если это ноль — наш цикл закончится

same => n,Set(WorkingQueue=${CUT(AvailableQueues,^,${QueueCounter})})

same => n,EndWhile()

; сообщаем агенту что он успешно вошел в систему

same => n,Playback(silence/1&agent-loginok)

same => n,Hangup()

exten => no_queues_available,1,Verbose(2,No queues available for $⁠{MemberChannel})

same => n,Playback(silence/1&channel&not-yet-assigned)

same => n,Hangup()

; ————————-

; Используется для выхода агентов из всех настроенных очередей согласно AstDB

exten => *56,1,Verbose(2,Logging out of multiple queues)

; Поскольку мы повторно использовали некоторый код, то поместили дубликат кода

; в подпрограмму

same => n,GoSub(subSetupAvailableQueues,start,1())

same => n,Set(QueueCounter=1)

same => n,Set(WorkingQueue=${CUT(AvailableQueues,^,${QueueCounter})})

same => n,While($[${EXISTS(${WorkingQueue})}])

same => n,RemoveQueueMember(${WorkingQueue},${MemberChanType}/${MemberChannel})

same => n,Set(QueueCounter=$[${QueueCounter} + 1])

same => n,Set(WorkingQueue=${CUT(AvailableQueues,^,${QueueCounter})})

same => n,EndWhile()

same => n,Playback(silence/1&agent-loggedoff)

same => n,Hangup()

; ————————-

; Используется для приостановки агентов во всех доступных очередях

exten => *72,1,Verbose(2,Pausing member in all queues)

same => n,GoSub(subSetupAvailableQueues,start,1())

; если мы не определяем очередь, участник останавливается во всех очередях

same => n,PauseQueueMember(,${MemberChanType}/${MemberChannel})

same => n,GotoIf($[${PQMSTATUS} = PAUSED]?agent_paused,1:agent_not_found,1)

exten => agent_paused,1,Verbose(2,Agent paused successfully)

same => n,Playback(silence/1&unavailable)

same => n,Hangup()

; ————————-

; Используется для возобновления агентов во всех доступных очередях

exten => *87,1,Verbose(2,UnPausing member in all queues)

same => n,GoSub(subSetupAvailableQueues,start,1())

; если мы не определяем очередь, участник возобновляется во всех очередях

same => n,UnPauseQueueMember(,${MemberChanType}/${MemberChannel})

same => n,GotoIf($[${UPQMSTATUS} = UNPAUSED]?

agent_unpaused,1:agent_not_found,1)

exten => agent_unpaused,1,Verbose(2,Agent paused successfully)

same => n,Playback(silence/1&available)

same => n,Hangup()

; ————————-

; Используется как в режиме паузы, так и возобновления

exten => agent_not_found,1,Verbose(2,Agent was not found)

same => n,Playback(silence/1&cannot-complete-as-dialed)

Вы сможете усовершенствовать эти процедуры входа и выхода, учитывая, что переменные канала AQMSTATUS и RQMSTATUS устанавливаются каждый раз когда используются AddQueueMember() и RemoveQueueMember(). Например, можно установить флаг, который позволит участнику очереди узнать, что он не был добавлен в очередь, установив флаг, или даже добавив запись или систему преобразования текста в речь для воспроизведения названия конкретной очереди, которая создает проблему. Или, если вы отслеживаете это через AMI, у вас может быть всплывающее окно, или используйте JabberSend() чтобы сообщить участнику очереди через мгновенный обмен сообщениями, или…(разве Asterisk не веселье?).

Введение в состояния устройств

Состояния устройств в Asterisk используются для информирования различными сообщениями о том, используется ли ваше устройство в настоящее время или нет. Это особенно важно для очередей, так как мы не хотим отправлять абонентов агенту, который уже находится на телефоне.6 Состояниями устройства управляет модуль канала и в Asterisk только chan_sip имеет соответствующую обработку. Когда очередь запрашивает состояние устройства, она сначала запрашивает драйвер канала (т.е. chan_sip). Если канал не может предоставить состояние устройства напрямую (как в случае с chan_iax2), он просит ядро Asterisk определить его, что и делается путем поиска по каналам, которые в настоящее время выполняются.

К сожалению, простое обращение к ядру с просьбой выполнить поиск по активным каналам не является точным, поэтому получение состояния устройства из каналов, отличных от chan_sip менее надежно при работе с очередями. Мы рассмотрим некоторые методы управления вызовами других типов каналов в “Расширенных очередях«, но пока сосредоточимся на SIP-каналах, которые не имеют сложных требований к состояниям устройств. Дополнительные сведения о состояниях устройств см. в Главе 14.

Для правильного определения состояния устройства в Asterisk необходимо включить счетчик вызовов в sip.conf. Включив счетчик вызовов, мы сообщаем Asterisk отслеживать активные вызовы для устройства, чтобы эта информация могла быть возвращена в модуль канала и состояние было точно отражено в наших очередях. Во-первых, давайте посмотрим, что происходит с вашей очередью без опции счетчика вызовов:

*CLI> queue show support

support has 0 calls (max unlimited) in ‘rrmemory’ strategy

(0s holdtime, 0s talktime), W:0, C:0, A:0, SL:0.0% within 0s

Members:

SIP/0000FFFF0001 (dynamic) (Not in use) has taken no calls yet

No Callers

Теперь предположим, что в нашем диалплане есть номер 555, который вызывает MusicOnHold(). Если мы набираем этот номер не включив счетчик вызовов, запрос очереди support (в которой SIP/0000FFFF0001 является участником) в Asterisk CLI покажет что-то подобное следующему:

— Executing [555@LocalSets:1] MusicOnHold(«SIP/0000FFFF0001-00000000»,

«») in new stack

— Started music on hold, class ‘default’, on SIP/0000FFFF0001-00000000

*CLI> queue show support

support has 0 calls (max unlimited) in ‘rrmemory’ strategy

(0s holdtime, 0s talktime), W:0, C:0, A:0, SL:0.0% within 0s

Members:

SIP/0000FFFF0001 (dynamic) (Not in use) has taken no calls yet

No Callers

Обратите внимание, что даже если наш телефон помечен как In Use (используемый), потому что он находится на вызове, он не отображается таковым когда мы смотрим на состояние очереди. Это является проблемой, так как очередь будет рассматривать это устройство как доступное, даже если оно уже находится на вызове.

Чтобы исправить эту проблему, нам нужно добавить callcounter=yes в раздел [general] нашего файла sip.conf. Мы также можем специально настроить его для любого шаблона или пира (так как это параметр конфигурации пира); однако это действительно то, что вы хотите установить для всех пиров, которые могут когда-либо быть частью очереди, поэтому лучше всего поместить эту опцию в раздел [general] или как часть каждого шаблона.

Отредактируйте свой sip.conf следующим образом:

[general]

context=unauthenticated ; контекст по умолчанию для входящих вызовов

allowguest=no ; отключить неаутентифицированные звонки

srvlookup=yes ; включить поиск DNS SRV записей для исходящих звонков

udpbindaddr=0.0.0.0 ; прослушивать UDP запросы на всех интерфейсах

tcpenable=no ; отключить поддержку TCP

callcounter=yes ; включить статусы устройств для SIP устройств

Затем перезагрузите модуль chan_sip и выполните тот же тест снова:

*CLI> sip reload

Reloading SIP

== Parsing ‘/etc/asterisk/sip.conf’: == Found

Теперь устройство должно отображаться при выполнении вызова c него:

== Parsing ‘/etc/asterisk/sip.conf’: == Found

== Using SIP RTP CoS mark 5

— Executing [555@LocalSets:1] MusicOnHold(«SIP/0000FFFF0001-00000001»,

«») in new stack

— Started music on hold, class ‘default’, on SIP/0000FFFF0001-00000001

*CLI> queue show support

support has 0 calls (max unlimited) in ‘rrmemory’ strategy

(0s holdtime, 0s talktime), W:0, C:0, A:0, SL:0.0% within 0s

Members:

SIP/0000FFFF0001 (dynamic) (In use) has taken no calls yet

No Callers

Короче говоря, Queue() должно знать состояние устройства для надлежащего управления распределением вызовов. Опция callcounter в sip.conf является важным компонентом правильно функционирующей очереди.

Файл queues.conf

Мы уже упоминали про файл queues.conf, который уже создан, но в нём есть много вариантов и мы решили, что было бы правильно и уместно рассмотреть некоторые из них.

Таблица 13-2 содержит опции, доступные в разделе [general] queues.conf.

Таблица 13-2. Доступные опции для раздела [general] queues.conf

Опция Возможные значения Описание
persistentmembers yes, no Установите значение yes для хранения динамически добавляемых элементов очереди в базе данных Asterisk, чтобы они автоматически добавлялись при перезапуске Asterisk.
autofill yes, no Если autofill отключено, приложение очереди будет пытаться доставлять вызовы агентам последовательно. Это означает, что одновременно предпринимается попытка передать агентам только один вызов. Дополнительные абоненты не беспокоят агентов, пока абонент не подключен к агенту. При включенном autofill абоненты распределяются между доступными агентами одновременно. Если вы хотите, чтобы это всегда было включено — yes.
monitor-type MixMonitor, <unspecified> Если этот параметр задан как MixMonitor, Asterisk будет использовать приложение MixMonitor() для записи вызовов в очереди. Если вы не укажете значение или не закомментируете параметр, приложение Monitor() будет использоваться для записи вызовов.
updatecdr yes, no Установите значение yes, чтобы при ответе агента на вызов очереди Asterisk заполнял поле dstchannel CDR именем участника очереди. Значение определяется при входе агента в систему с помощью приложения AddQueueMember(). Эта опция используется для имитации поведения каналов chan_agent в CDR.
shared_lastcall yes, no Это значение важно, если участники вошли в несколько очередей. Оно гарантирует, что ссылка на их последний вызов совместно используется во всех очередях, что гарантирует что все очереди будут уважать время отдыха других очередей

Таблица 13-3 описывает опции, доступные для настройки, определенные для каждой очереди.

Таблица 13-3. Доступные параметры для определенных очередей в queues.conf

Опция Возможные значения Описание
musicclass Класс музыки как определено в musiconhold.conf Задает класс музыки, используемый определенной очередью. Это значение также можно переопределить для каждого вызова с помощью переменной CHANNEL(musicclass).
announce Имя файла объявления Используется для воспроизведения объявления агенту, который отвечает на вызов, как правило, чтобы сообщить ему, из какой очереди поступает абонент. Полезно, когда агент находится в нескольких очередях, особенно когда вызовы из очереди настроены на автоответ. Имейте в виду, что время, необходимое для воспроизведения этой записи, добавляется к времени ожидания вызывающего абонента (так как он не может ее услышать). Также учтите, что ваши агенты будут слышать эту запись несколько раз в день и не получат пользу от длинного сообщения. Если вы используете эту опцию, делайте записи короткими; одно или два слова максимум (и не более секунды в длину).
strategy ringall, leastrecent, fewestcalls, random, rrmemory, linear, wrandom ringall Звонят все доступные участники (по умолчанию). Эта стратегия распределения на самом деле не считается АРВ. В традиционных терминах телефонии она известна как группа вызова. leastrecent Звонит интерфейс, который недавно получил звонок. В очереди, где есть много вызовов приблизительно той же продолжительности, это может сработать. Не сработает, если агент был на вызове в течение часа, и все его коллеги получили свой последний звонок 30 минут назад, потому что агент, который только что закончил 60-минутный вызов, получит следующий. fewestcalls Звонит интерфейс, который завершил наименьшее количество вызовов в этой очереди. Это может быть несправедливо, если вызовы не всегда одинаковой продолжительности. Агент мог обработать три звонка по 15 минут каждый, а его коллега-четыре 5-секундных; агент, который обработал три звонка, получит следующий. random Звонит случайный интерфейс. Это действительно может работать очень хорошо и очень справедливо с точки зрения равномерного распределения вызовов между агентами. rrmemory Прозвон участников в круговой манере, помня, где он остановился последним для следующего абонента. Это также может быть очень справедливым, но не настолько как random. linear Прозвон участников в указанном порядке, всегда начиная с начала списка. Это работает если у вас есть команда, в которой есть агенты, которые должны обрабатывать большинство вызовов, и другие агенты, которые должны получать вызовы, только если основные агенты заняты. wrandom Звонок случайному участнику, но используется пенальти участников в качестве веса. Стоит рассмотреть в большой очереди со сложным взвешиванием среди агентов.
servicelevel значение в секундах Эта настройка не так полезна, как должно быть. Идея в том, чтобы определить максимально допустимое время ожидания до ответа. Затем обратите внимание, сколько вызовов отвечают в пределах этого порога, и идут к вашему уровню обслуживания. Так, например, если ваш уровень обслуживания составляет 60 секунд, и 4 из 5 вызовов отвечают за 60 секунд или меньше, ваш уровень обслуживания составляет 80%. Причина, по которой эта метрика не так полезна в Asterisk, заключается в том, что она не разбита каким-либо полезным способом (например, по часам или дням недели). Вместо этого, она дает вам порог для всех звонков с момента последней перезагрузки app_queue. Это хорошая идея, но не совсем практичная.
context контекст диалплана Позволяет абоненту выйти из очереди, нажав одну цифру DTMF. Если контекст задан и вызывающий абонент вводит цифру, то эта цифра попытается быть сопоставленной в указанном контексте, и выполнение диалплана продолжится там. Обратите внимание, что при этом абонент также теряет свое место в очереди.
penaltymemberslimit Значение 0 или больше Используется для игнорирования штрафных значений, если число участников в очереди меньше указанного значения.
timeout Значение в секундах Указывает количество секунд для вызова устройства участника. См. также timeoutpriority.
retry Значение в секундах Указывает количество секунд ожидания перед попыткой вызова следующего участника очереди, если значение timeout исчерпано при попытке позвонить участнику очереди.
timeoutpriority app,conf Используется для управления приоритетом двух возможных вариантов timeout, заданных для очереди. Приложение Queue() имеет значение тайм-аута, которое можно указать для управления абсолютным временем нахождения вызывающего абонента в очереди. Значение timeout в queues.conf управляет временем (наряду с повторной попыткой) для вызова участника. Иногда эти значения конфликтуют, поэтому вы можете контролировать, какое значение имеет приоритет. По умолчанию это app, так как оно работает в предыдущих версиях.
weight Значение 0 или выше Определяет вес очереди. Очередь с более высоким определенным весом будет иметь приоритет, если участники связаны с несколькими очередями. Имейте в виду, что если у вас очень загруженная очередь с большим весом, абоненты в очереди с меньшим весом могут никогда не получить ответа (или им придется долго ждать).
wrapuptime Значение в секундах Количество секунд, в течение которого участник остается недоступным в очереди после завершения вызова. Это время позволяет агенту завершить любую обработку после вызова, которая может потребоваться, прежде чем перейти к следующим вызовам.
autofill yes,no То же, что определено в разделе [general]. Это значение может быть определено для каждой очереди.
autopause yas,no,all Включает/отключает автоматическую приостановку участников, не отвечающих на вызов. Значение all приводит к приостановке этого участника во всех очередях. Этот параметр может быть сложным в реальной среде, потому что если агент не знает, что он был приостановлен, вы можете оказаться с агентами, ожидающими звонков, не зная, что они были приостановлены. Никогда не используйте это, если у вас нет способа указать участникам, что они были приостановлены, или у вас есть супервизор, который наблюдает за состоянием очереди в режиме реального времени. Также проблема в том, что агенты должны быть обучены тому, что ответ на вызов из очереди не является необязательным; очередь ожидает, что они ответят на каждый вызов, который она им представляет.
maxlen Значение 0 или выше Указывает максимальное число абонентов, которым разрешено ожидать в очереди. Нулевое значение означает что в очереди разрешено неограниченное число абонентов.
announce-frequency Значение в секундах Определяет, как часто мы должны объявлять позицию вызывающего абонента и/или предполагаемое время ожидания в очереди. Установите это значение в ноль, чтобы отключить. В небольшом колл-центре маловероятно, что система сможет сделать точные оценки и, таким образом, абоненты с большей вероятностью сочтут эту информацию разочаровывающей.
min-announce-frequency Значение в секундах Определяет минимальное время, которое должно пройти, прежде чем мы снова объявляем позицию абонента в очереди. Это используется, когда положение вызывающего абонента может часто меняться, чтобы абонент не слышал несколько обновлений в течение короткого периода времени.
periodic-announce-frequency Значение в секундах Указывает как часто мы должны делать периодические объявления вызывающему абоненту. Имейте в виду, что воспроизведение сообщения абонентам на регулярной основе будет расстраивать их, поэтому подумайте о том, чтобы а) это сообщение было коротким и б) не воспроизводилось слишком часто. Приятная музыка будет более удачным вариантом, чем бесконечно повторяющиеся извинения или реклама.
random-periodic-announce yes,no Если задано значение yes, будут воспроизводиться определенные периодические объявления в случайном порядке. См. periodic-announce.
relative-periodic-announce yes,no Если задано значение yes, таймер periodic-announce-frequency будет запускаться с момента окончания воспроизводимого файла, а не с самого начала. По умолчанию no.
announce-holdtime yes,no,once Определяет, должно ли расчетное время ожидания воспроизводиться вместе с периодическими объявлениями. Можно установить значение yes, no или once (один раз).
announce-position yes,no,limit,more Определяет, должна ли быть объявлена позиция вызывающего абонента в очереди. Если no, положение не будет объявлено. Если задано значение yes, позиция вызывающего абонента всегда будет объявлена. Если установлено значение limit, звонящий услышит свою позицию в очереди, только если он находится в пределах, определенных announce-position-limit. Если значение больше, звонящий услышит его положение, если он не входит в число, определяемое announce-position-limit. Если в вашей системе есть какая-либо логика, которая может повысить абонентов по рангу (т. е. высокоприоритетные вызовы перемещаются в начало очереди), лучше не использовать эту опцию. Редко что расстраивает звонящего больше, чем уведомление о том, что его переместили в конец очереди.
announce-position-limit Число 0 или выше Используется, если вы определили announce-position как limit или more.
announce-round-seconds Значение в секундах Если это значение не равно нулю, объявляется число секунд и округляется до заданного значения.
queue-thankyou Имя файла подсказки для воспроизведения Если значение не определено, воспроизводится значение по умолчанию (”Благодарим за терпение»). Если задано пустое значение, приглашение не будет воспроизводиться вообще.
queue-youarenext Имя файла подсказки для воспроизведения Если значение не определено, воспроизводится значение по умолчанию (”Ваш звонок является первым в очереди и будет отвечен первым доступным оператором»). Если задано пустое значение, приглашение не будет воспроизводиться вообще.
queue-thereare Имя файла подсказки для воспроизведения Если не определено, воспроизводит значение по умолчанию (”Ваша позиция в очереди»). Если задано пустое значение, приглашение не будет воспроизводиться вообще.
queue-callswaiting Имя файла подсказки для воспроизведения Если не определено, воспроизводится значение по умолчанию (”Ожидайте, пожалуйста, ответа оператора»). Если задано пустое значение, приглашение не будет воспроизводиться вообще
queue-holdtime Имя файла подсказки для воспроизведения Если значение не определено, воспроизводится значение по умолчанию (“Прогнозируемое время ожидания составляет”). Если задано пустое значение, приглашение не будет воспроизводиться вообще.
queue-minutes Имя файла подсказки для воспроизведения Если значение не определено, воспроизводится значение по умолчанию («минут»). Если задано пустое значение, приглашение не будет воспроизводиться вообще.
queue-seconds Имя файла подсказки для воспроизведения Если значение не определено, воспроизводится значение по умолчанию («секунд»). Если задано пустое значение, приглашение не будет воспроизводиться вообще.
queue-reporthold Имя файла подсказки для воспроизведения Если значение не определено, воспроизводится значение по умолчанию («Время ожидания»). Если задано пустое значение, приглашение не будет воспроизводиться вообще.
periodic-announce Набор периодических объявлений для воспроизведения, разделенные запятыми Подсказки воспроизводятся в том порядке, в котором они определены. По умолчанию используется queue-periodic-announce (“Все наши операторы заняты, пожалуйста, оставайтесь на линии и Ваш звонок будет обслужен первым доступным оператором»).
monitor-format gsm, wav, wav49, <любой действительный формат> Задает формат файла, используемый при записи. Если monitor-format закомментирован, вызовы не будут записываться.
monitor-type MixMonitor То же, что и monitor-type, определенный в разделе [general], но для каждой очереди. Если вам нужно старое поведение монитора, просто удалите или отметьте эту опцию полностью
joinempty paused, penalty, inuse, ringing, unavailable, invalid, unknown, wrapup Управляет добавлением абонента в очередь при отсутствии доступных участников. Параметры, разделенные запятыми, можно включить, чтобы определить, как этот параметр определяет доступность элементов. Определения значений: paused Участники считаются недоступными, если они приостановлены. penalty Участники считаются недоступными, если их штрафы меньше, чем QUEUE_MAX_PENALTY. inuse Участники считаются недоступными, если status их устройства In Use. ringing Участники считаются недоступными, если состояние их устройства Ringing. unavailable Применяется в основном к каналам агента; если агент не вошел в систему, но является участником очереди, канал считается недоступным. invalid Участники считаются недоступными, если их состояние устройства Invalid. Как правило, это условие ошибки. unknown Участники считаются недоступными, если состояние их устройств неизвестно. wrapup Участники считаются недоступными, если они находятся в данный момент во времени отдыха после завершения вызова.
leavewhenempty paused, penalty, inuse, ringing, unavailable, invalid, unknown, wrapup Используется для контроля того, будут ли абоненты выброшены из очереди, когда участники больше не могут принимать вызовы. Смотрите joinempty для получения дополнительной информации о присваиваемых значениях.
eventwhencalled yes, no, vars Если задано значение yes, следующие события диспетчера будут отправлены в интерфейс управления Asterisk (AMI): AgentCalled AgentDump AgentConnect AgentComplete Если установлено значение vars, все переменные канала, связанные с агентом, также будут отправлены в AMI.
eventmemberstatus yes,no Если задано значение yes, событие QueueMemberStatus будет отправлено в AMI. Обратите внимание, что это может генерировать много событий менеджера.
reportholdtime yes,no Позволяет сообщать о времени удержания вызывающего абонента участнику очереди до установления моста. Имейте в виду, что воспроизведение этой информации для агента потребует времени, которое для вызывающего абонента будет представлять еще большее время удержания.
ringinuse yes,no Используется для предотвращения отправки вызовов участникам, состояние которых In Use. Напомним из нашего обсуждения в предыдущем разделе, что только драйвер канала SIP в настоящее время может точно сообщить об этом состоянии.
memberdelay Значение в секундах Используется если требуется задержка перед соединением вызывающего абонента и участника очереди.
timeoutrestart yes,no Если задано значение yes, сбрасывает время ожидания ответа агента при получении от канала состояния BUSY или CONGESTION. Это может быть полезно, если агенту разрешено отклонить или отменить вызов.
defaultrule Правило определенное в queuerules.conf Привязывает правило очереди, как определено в queuerules.conf к этой очереди; используется для динамического изменения минимальных и максимальных штрафов, которые затем используются для выбора доступного агента. См. “Динамическое изменение штрафов (queuerules.conf)«.
member Устройство Используется для определения статических участников в очереди. Для определения статического элемента необходимо указать его Technology/Device_ID (напр., Agent/1234, SIP/0000FFFF0001, DAHDI/g0/14165551212).

Значения, приведенные в Таблице 13-4 связаны с соединением вызова и участника очереди. Эти переменные могут быть полезны для целей регистрации; однако их нельзя использовать для принятия решений о маршрутизации, поскольку они устанавливаются только в тот момент, когда вызов соединен (т.е. агент отвечает), и, к сожалению, не во время вызова агента (агент звонит).

Таблица 13-4. Параметры мостового соединения

Опция Возможные значения Описание
setinterfacevar yes Если задано значение yes, следующие переменные канала будут установлены непосредственно перед соединением абонента с участником очереди: MEMBERINTERFACE Интерфейс участника, например Agent/1234 MEMBERNAME Имя участника MEMBERCALLS Количество вызовов, выполненных интерфейсом MEMBERLASTCALL Последний раз, когда участник принимал вызов MEMBERPENALTY Штрафное значение участника MEMBERDYNAMIC Указывает, был ли элемент динамически добавлен в очередь MEMBERREALTIME Указывает, включен ли участник в режиме Realtime или нет
setqueueentryvar yes,no Если установлено в yes, следующие переменные канала будут установлены непосредственно перед соединением: QEHOLDTIME Время удержания вызывающего абонента в очереди QEORIGINALPOS Позиция, с которой абонент первоначально вошел в очередь
setqueuevar yes,no Если установлено в yes, следующие переменные канала будут установлены непосредственно перед соединением вызова: QUEUENAME Имя очереди QUEUEMAX Максимальное число вызовов, разрешенных в этой очереди QUEUESTRATEGY Метод стратегии, определенный для очереди QUEUECALLS Количество вызовов, находящихся в очереди QUEUEHOLDTIME Текущее среднее время удержания абонентов в очереди QUEUECOMPLETED Количество завершенных вызовов в этой очереди QUEUEABANDONED Количество прерванных звонков QUEUESRVLEVEL Уровень обслуживания очереди QUEUESRVLEVELPERF Производительность очереди на уровне обслуживания
membermacro Имя макроса, определенного в диалплане Определяет макрос, который будет выполняться непосредственно перед соединением вызывающего объекта и участника очереди. Этот макрос можно использовать с предыдущими параметрами (которые задают дополнительные переменные канала), что позволяет оценить состояние очереди в системе в момент доставки вызова агенту. Если это вас интересует, убедитесь, что вместо этого вы используете локальный канал Asterisk (определите участников очереди как локальные каналы). Затем вы сможете реализовать решения о маршрутизации, которые выполняются непосредственно перед тем, как вызов будет предоставлен агенту, что может дать вам гораздо больше контроля над очередями чем app_queue может предоставить самостоятельно.

Файл agents.conf

Если вы просматривали примеры в каталоге ~/src/asterisk-complete/11/configs вы, возможно, заметили файл agents.conf. Он может показаться заманчивым и у него есть свои места, но в целом лучший способ реализовать очереди — это использование каналов SIP. Для этого есть две причины. Во-первых, SIP-каналы являются единственным типом, который предоставляет истинную информацию о состоянии устройств. Другая причина заключается в том, что каналы агентов всегда подключаются при входе в систему, и если вы используете удаленных агентов, требования к пропускной способности для всех этих каналов могут быть больше, чем вы ожидаете. Однако в загруженных колл-центрах может быть желательно заставить агентов отвечать на вызовы немедленно, а не заставляя их нажимать кнопку ответа на телефоне и агентские каналы могут быть полезны для этого в некоторых случаях (мы все же предпочли бы использовать SIP каналы с автоответом, установленным на аппарате).

Файл agents.conf используется для определения агентов очередей использованием агентских каналов. Этот канал по своей природе похож на другие типы каналов в Asterisk (Local, SIP, IAX2 и т.д.), но это скорее псевдоканал в том смысле, что он используется для подключения вызывающих абонентов к агентам, которые вошли в систему с использованием других типов транспортных каналов. Например, предположим, что мы используем наш SIP-телефон для входа в Asterisk с помощью приложения диалплана AgentLogin(). Как только мы вошли в систему канал остается открытым все время, пока он доступен (в системе), а затем ему передаются вызовы по агентскому каналу на телефон.

Давайте посмотрим на различные параметры, доступные нам в файле agent.conf, чтобы лучше понять, что он нам предоставляет. Таблица 13-5 показывает единственную опцию, доступную в разделе [general] agent.conf. Таблица 13-6покажет доступные опции под заголовком [agents].

Таблица 13-5. Параметры доступные под заголовком [general] в agents.conf

Опция Возможные значения Описание
multiplelogin yes,no Если задано значение yes, одна линия на устройстве может входить в систему как несколько агентов. По умолчанию yes.

Таблица 13-6. Параметры, доступные под заголовком [agents] в agents.conf

Опция Возможные значения Описание
maxloginretries Целочисленное значение Указывает максимальное число попыток, которое агент должен выполнить для входа в систему, прежде чем система сочтет попытку неудачной и завершит вызов. По умолчанию 3. Это не блокирует учетную запись, поэтому пользователь может немедленно повторить попытку так часто, как он хочет.
autologoff Значение в секундах Указывает количество секунд, в течение которых устройство агента должно звонить, прежде чем агент автоматически выйдет из системы.
autologoffunavail yes,no Если задано значение yes, агент автоматически выходит из системы, если вызываемое устройство возвращает состояние CHANUNAVAIL.
ackcall yes,no Если задано значение yes, агент должен ввести одну цифру DTMF чтобы принять вызов. Должно использоваться совместно с acceptdtmf. По умолчанию no.
acceptdtmf Одиночный символ DTMF Используется в сочетании с ackcall, этот параметр определяет DTMF символ, который будет использоваться для принятия вызова. По умолчанию #.
endcall yes,no Если установлено yes, позволяет агенту завершить вызов одной цифрой DTMF. Должно использоваться совместно с enddtmf. По умолчанию yes.
enddtmf Одиночный символ DTMF Используется с endcall, этот параметр определяет символ DTMF, который будет использоваться для завершения вызова. По умолчанию *.
wrapuptime Значение в миллисекундах Указывает время после отключения вызывающего абонента, в течение которого агент не сможет принять другой вызов. Используется, когда агенты должны выполнять функцию после каждого вызова (например, ввод сведений о вызове в журнал).
musiconhold Класс музыки определенный в misuconhold.conf Определяет прослушиваемые агентами класс музыки по умолчанию при входе в систему.
goodbye Имя файла (относительно /var/lib/asterisk/sounds/ <lang>) Определяет звук прощания по умолчанию, воспроизводимый агентам. По умолчанию vm-goodbye.
updatecdr yes,no Используется в CDRs для изменения поля исходного канала на agent/agent_id.
group Целочисленное значение Позволяет определить группы для наборов агентов. Использование групп агентов является несколько устаревшей функциональностью, которую мы не рекомендуем использовать. Если вы определяете group1, Вы можете использовать Agent/@1 в queues.conf для вызова этой группы агентов. Вызов будет подключен произвольно к одному из этих агентов. Если никакие агенты не доступны, то возвратится назад к очереди как любой другой неотвеченный вызов. Если вы используете Agent/:1, то он будет ждать пока участник группы станет доступным. Использование стратегий не влияет на группы агентов. Не используйте их.
recordagentcalls yes,no Включает/отключает запись вызовов агента. По умолчанию отключено.
recordformat Формат файла (gsm, wav, и тд) Определяет формат, используемый для записи вызовов агента. По умолчанию wav.
urlprefix Строка (URL) Принимает строку в качестве аргумента. Строка может быть сформирована в виде URL и добавлена к началу текста, который будет добавлен к имени записи.
savecallsin Путь файловой системы (напр. /var/calls) Принимает путь к файловой системе в качестве аргумента. Позволяет переопределить путь по умолчанию /var/spool/asterisk/monitor одним из выбранных вами параметров.a
custom_beep Имя файла (относительно /var/lib/asterisk/sounds/ <lang>) Принимает имя файла в качестве аргумента. Может использоваться для определения пользовательского тона уведомления, чтобы сигнализировать всегда подключенному агенту о входящем вызове. В идеале, выбирайте короткую запись (не более нескольких сотен миллисекунд).
agent Определение агента (см. описание) Где резина попадает на дорогу. Этот параметр можно повторять так часто, как требуется. Каждый экземпляр определяет агента для использования Queue() и AgentLogin(). Это агенты, которые будут входить в систему и оставаться подключенными к системе, ожидая вызовов, которые будут доставлены приложением Queue(). Агенты определяются следующим образом: agent => agent_id,agent_password,name Пример определения агента: agent => 1000,1234,Danielle Roberts

a Поскольку для хранения вызовов потребуется большой объем места на жестком диске, вам потребуется определить стратегию для хранения и управления этими записями. Это место, вероятно, должно располагаться на отдельном томе с очень высокими характеристиками.

Расширенные очереди

В этом разделе мы рассмотрим некоторые более тонкие элементы управления очередью, такие как параметры для управления объявлениями и когда абоненты должны быть помещены (или удалены) в очередь. Мы также рассмотрим штрафы и приоритеты, исследуя методы управления агентами в нашей очереди, отдавая предпочтение пулу агентов для ответа на вызов и динамически увеличивая этот пул на основе времени ожидания в очереди. Наконец, мы рассмотрим использование локальных каналов в качестве участников очереди, что дает нам возможность выполнять функции диалплан до подключения вызывающего абонента к агенту.

Приоритетная очередь (взвешивание очереди)

Иногда вам нужно добавить людей в очередь с более высоким приоритетом, чем у других абонентов. Возможно, вызывающий абонент уже провел время в очереди, а агент получил некоторую информацию, но понял, что абонент должен быть переведен в другую очередь. В этом случае, чтобы минимизировать общее время ожидания абонента, может быть желательно перевести вызов в приоритетную очередь, которая имеет больший вес (и, следовательно, более высокое предпочтение), поэтому на этот вызов будет дан быстрый ответ.

Установка более высокого приоритета в очереди выполняется с помощью параметра weight (вес). Если у вас есть две очереди с разными весами (например, support и support-priority) агентам, назначенным в обе очереди, будут переданы вызовы из очереди с более высоким приоритетом, а не из очереди с низким приоритетом. Эти агенты не будут принимать вызовы из очереди с более низким приоритетом, пока очередь с высоким приоритетом не будет очищена. (Обычно есть некоторые агенты, которые назначаются только в очередь с более низким приоритетом чтобы гарантировать своевременную обработку этих вызовов.) Например, если мы поместим участника очереди Джеймса Шоу и в support и в support-priority звонки из очереди support-priority будут иметь предпочтительное положение для Джеймса по сравнению с вызывающими в очереди support.

Давайте посмотрим, как мы могли бы сделать эту работу. Во-первых, нам нужно создать две одинаковые очереди, за исключением опции weght. Мы можем использовать шаблон для этого, чтобы гарантировать, что две очереди останутся идентичными, если что-то должно будет изменяться в будущем:

[support_template](!)

musicclass=default

strategy=rrmemory

joinempty=no

leavewhenempty=yes

ringinuse=no

[support](support_template)

weight=0

[support-priority](support_template)

weight=10

С нашими настроенными очередями (и впоследствии перезагруженными с помощью module reload app_queue.so из консоли Asterisk) мы можем теперь создать два расширения для передачи абонентов. Это можно сделать в любом месте, где вы обычно размещаете логику диалплана для выполнения трансфера. Мы собираемся использовать контекст LocalSets, который мы ранее включили в качестве начального контекста для наших устройств:

[LocalSets]

include => Queue ; разрешить прямой перевод вызовов в очереди

[Queues]

exten => 7000,1,Verbose(2,Entering the support queue)

same => n,Queue(support) ; стандартная очередь support доступна

; с номера 7000

same => n,VoiceMail(7000@queues,u) ; если нет участников в очереди мы

; выходим и отправляем абонента на

; голосовую почту

same => n,Hangup()

exten => 8000,1,Verbose(2,Entering the priority support queue)

same => n,Queue(support-priority) ; приоритетная очередь доступна по

; номеру 8000

same => n,VoiceMail(7000@queues,u) ; если нет участников в очереди мы

; выходим и отправляем абонента на

; голосовую почту

same => n,Hangup()

Вот и все: две очереди с разными весами. Мы настроили наши стандартные очереди для запуска с номера 7000, а наши приоритетные очереди — с 8000. Мы можем отразить это для нескольких очередей, просто сопоставив диапазоны 7XXX и 8XXX. Так, например, если у нас есть очередь sales с номером 7004, наша очередь priority-sales (для постоянных клиентов) может быть помещена в зеркальную очередь на 8004, которая имеет больший вес.

Единственная оставшаяся конфигурация — убедиться, что некоторые или все участники очереди помещены в обе очереди. Если у вас больше абонентов в очередях 7XXX, возможно вы захотите, иметь больше участников в этой очереди, а часть участников вашей будет входить в обе очереди. Как именно вы хотите настроить свои очереди будет зависеть от вашей локальной политики и обстоятельств.

Приоритет участника очереди

В пределах очереди мы можем оштрафовать участников, чтобы снизить их предпочтение в вызовах, когда в определенной очереди ожидают люди. Например, мы можем оштрафовать участников очереди, когда хотим чтобы они были участниками очереди, но принимать вызовы только тогда, когда очередь заполнится настолько, что все наши предпочтительные агенты будут недоступны. Это означает что у нас может быть три очереди (скажем support, sales и billing), каждая из которых содержит тех же трех участников очереди: Джеймс Шоу, Кей Мэдсен и Даниэль Робертс.

Предположим, мы хотим, чтобы Джеймс Шоу был предпочтительным контактом в очереди support, Кей Мэдсен — в sales, а Даниэль Робертс — в billing. Наказывая Кея Мэдсена и Даниэля Робертса в support, мы гарантируем, что Джеймс Шоу будет предпочтительным участником вызываемой очереди. Точно так же мы можем наказать Джеймса Шоу и Даниэля Робертса в sales, поэтому Кей Мэдсен предпочтительнее, и наказать Джеймса Шоу и Кея Мэдсена в billing, поэтому Даниэль Робертс предпочтительнее.

Наказание участников очереди может быть выполнено либо в файле queues.conf (если вы статически указываете участников очереди), либо через приложение диалплана AddQueueMember(). Давайте посмотрим, как наши очереди будут настроены со статическими участниками в queues.conf. В нашем примере предполагается, что ваш queues.conf по-прежнему содержит шаблон StandardQueue, который мы определили ранее в этой главе:

[support](StandardQueue)

member => SIP/0000FFFF0001,0,James Shaw ; предпочтительный

member => SIP/0000FFFF0002,10,Kay Madsen ; второй предпочтительный

member => SIP/0000FFFF0003,20,Danielle Roberts ; наименее предпочтительный

[sales](StandardQueue)

member => SIP/0000FFFF0002,0,Kay Madsen

member => SIP/0000FFFF0003,10,Danielle Roberts

member => SIP/0000FFFF0001,20,James Shaw

[billing](StandardQueue)

member => SIP/0000FFFF0003,0,Danielle Roberts

member => SIP/0000FFFF0001,10,James Shaw

member => SIP/0000FFFF0002,20,Kay Madsen

Определяя различные штрафы для каждого участника очереди,7 мы можем помочь контролировать предпочтение, куда доставляются абоненты, но при этом гарантировать, что другие участники очереди будут доступны для ответа на вызовы если предпочтительный участник недоступен. Штрафы также могут быть определены с помощью AddQueueMember(), как показано в следующем примере:

exten => *54,1,Verbose(2,Logging In Queue Member)

same => n,Set(MemberChannel=${CHANNEL(channeltype)}/${CHANNEL(peername)})

; *CLI> database put queue support/0000FFFF0001/penalty 0

same => n,Set(QueuePenalty=${DB(queue/support/${CHANNEL(peername)}/penalty)})

; *CLI> database put queue support/0000FFFF0001/membername «James Shaw»

same => n,Set(MemberName=${DB(queue/support/${CHANNEL(peername)}/membername)})

; AddQueueMember(queuename[,interface[,penalty[,options[,membername

; [,stateinterface]]]]])

same => n,AddQueueMember(support,${MemberChannel},

${QueuePenalty},,${MemberName})

Используя AddQueueMember() мы показали, как можно получить штраф, связанный с именем участника для конкретной очереди и присвоить это значение участнику когда он регистрируется в очереди. Для работы с несколькими очередями потребуется дополнительная абстракция; дополнительные сведения см. В разделе «Автоматическая регистрация в нескольких очередях«.

Динамическое изменение штрафов (queuerules.conf)

Используя файл queuerules.conf можно указать правила для изменения значений переменных канала QUEUE_MIN_PENALTY и QUEUE_MAX_PENALTY. Переменные QUEUE_MIN_PENALTY и QUEUE_MAX_PENALTY используются для управления предпочтительными участниками очереди для обслуживания абонентов. Допустим, у нас есть очередь support и есть пять участников очереди с различными штрафами в диапазоне от 1 до 5. Если до того как вызывающий абонент войдет в очередь, для переменной канала QUEUE_MIN_PENALTY будет задано значение 2, а для QUEUE_MAX_PENALTY — значение 4, только те участники очереди, для которых установлены штрафы со значениями в диапазоне от 2 до 4 будут считаться доступными для ответа на этот вызов:

[Queues]

exten => 7000,1,Verbose(2,Entering the support queue)

same => n,Set(QUEUE_MIN_PENALTY=2) ; устанавливает минимальный штраф участников

same => n,Set(QUEUE_MAX_PENALTY=4) ; устанавливает максимальный штраф участников

same => n,Queue(support) ; вход в очередь с использованием минимальных

; и максимальных штрафов участников

Более того, во время пребывания абонента в очереди мы можем динамически изменять значения QUEUE_MIN_PENALTY и QUEUE_MAX_PENALTY для этого абонента. Это позволяет использовать несколько или другой набор участников очереди в зависимости от того, как долго абонент ожидает в очереди. Например, в предыдущем примере мы могли бы изменить минимальный штраф на 1 и максимальный — на 5, если вызывающий должен ждать более 60 секунд в очереди. Правила определяются с помощью файла queuerules.conf. Несколько правил могут быть созданы для облегчения различных изменений штрафа на протяжении всего вызова. Давайте посмотрим, как мы определим изменения, описанные в предыдущем абзаце:

[more_members]

penaltychange => 60,5,1

Если вы вносите изменения в файл queuerules.conf и перезагружаете app_queue таким образом, новые правила будут влиять только на новых абонентов, входящих в очередь, а не на существующих, которые уже удерживаются.

Мы определили правило more_members в queuerules.conf и передали следующие значения для изменения штрафа:

60

Количество секунд ожидания перед изменением значений штрафа.

5

Новое значение QUEUE_MAX_PENALTY.

1

Новое значение QUEUE_MIN_PENALTY.

С нашим новым определенным правилом мы должны перезагрузить app_queue, чтобы применить изменения:

*CLI> module reload app_queue.so

— Reloading module ‘app_queue.so’ (True Call Queueing)

== Parsing ‘/etc/asterisk/queuerules.conf’: == Found

Мы также можем проверить наши правила на консоли с помощью queue show rules:

*CLI> queue show rules

Rule: more_members

After 60 seconds, adjust QUEUE_MAX_PENALTY to 5 and QUEUE_MIN_PENALTY to 1

Теперь, когда наше правило загружено в память, мы можем изменить диалплан, чтобы использовать его. Просто измените строку Queue() так, чтобы включить новое правило:

[Queues]

exten => 7000,1,Verbose(2,Entering the support queue)

same => n,Set(QUEUE_MIN_PENALTY=2) ; устанавливает миним. штраф участников

same => n,Set(QUEUE_MAX_PENALTY=4) ; устанавливает макс. штраф участников

; Queue(queuename[,options[,URL[,announceoverride[,timeout[,AGI[,macro

; [,gosub[,rule[,position]]]]]]]]])

same => n,Queue(support,,,,,,,,more_members) ; вход в очередь с

; использованием мин и

; максимальных штрафов участников

Файл queues.conf достаточно гибкий. Мы можем определить наше правило, используя относительные, а не абсолютные значения штрафа и также можем определить несколько правил:

[more_members]

penaltychange => 30,+1

penaltychange => 45,,-1

penaltychange => 60,+1

penaltychange => 120,+2

Здесь мы изменили наше правило more_members, чтобы использовать относительные значения. Через 30 секунд мы увеличиваем максимальный штраф на 1 (что приведет нас к 5, используя наш пример диалплана). После 45 секунд, мы уменьшаем минимальный штраф на 1 и так далее. Мы можем проверить изменения нашего нового правила после module reloadapp_queue.so в консоли Asterisk:

*CLI> queue show rules

Rule: more_members

After 30 seconds, adjust QUEUE_MAX_PENALTY by 1 and QUEUE_MIN_PENALTY by 0

After 45 seconds, adjust QUEUE_MAX_PENALTY by 0 and QUEUE_MIN_PENALTY by -1

After 60 seconds, adjust QUEUE_MAX_PENALTY by 1 and QUEUE_MIN_PENALTY by 0

After 120 seconds, adjust QUEUE_MAX_PENALTY by 2 and QUEUE_MIN_PENALTY by 0

Управление объявлениями

Asterisk имеет возможность воспроизводить несколько объявлений для абонентов, ожидающих в очереди. Например, вы можете объявить позицию вызывающего абонента в очереди, среднее время ожидания или сделать периодические объявления благодарящие абонентов за ожидание (или что бы там ни говорили ваши аудиофайлы). Важно тщательно настроить значения, контролирующие воспроизведение этих объявлений для вызывающих абонентов, потому что объявление их позиции, благодарность за ожидание и слишком частое информирование их о среднем времени удержания будет раздражать их, что не задумано этими функциями.

Воспроизведение объявлений между файлами музыки на удержание

Вместо обработки сложных объявлений для каждой из ваших очередей, вы могли бы альтернативно (или совместно) использовать функцию объявления, определенную в musiconhold.conf. Перед воспроизведением файла для музыки будет воспроизведен файл объявления, и затем он воспроизведется снова между аудиофайлами. Скажем, например, у вас есть 5-минутный цикл аудио, но вы хотите воспроизводить сообщение «Спасибо за ожидание» каждые 30 секунд. Вы можете разделить аудиофайл на 30-секундные сегменты, установить их имена файлов, начиная с 00-, 01-, 02- и т.д. (Чтобы они воспроизводились по порядку), а затем определить объявление. В musiconhold.conf класс может выглядеть примерно так:

[moh_jazz_queue]

mode=files

sort=alpha

announcement=queue-thankyou

directory=moh_jazz_queue

Есть несколько вариантов в файле queues.conf, которые можно использовать для тонкой настройки того, что и когда объявления воспроизводятся абонентам. Полный список параметров очереди доступен в «Файл queues.conf«, но мы рассмотрим соответствующие здесь.

Таблица 13-7 содержит список параметров, которые вы можете использовать, чтобы контролировать, когда объявления воспроизводятся абоненту.

Таблица 13-7. Функции связанные с контролем времени в очереди

Опция Возможные значения Описание
announce-frequency Значение в секундах Определяет как часто мы должны объявлять позицию абонента и/или расчетное время удержания очереди. Установите это значение на ноль, чтобы отключить.
min-announce-frequency Значение в секундах Указывает минимальное количество времени, которое должно пройти прежде чем объявить позицию абонента в очереди снова. Используется когда позиция абонента может часто меняться, чтобы предотвратить прослушивание нескольких объявлений за короткий промежуток времени
periodic-announce-frequency Значение в секундах Указывает, как часто нужно делать периодические объявления вызывающей стороне.
random-periodic-announce yes,no Если установлено yes, будут воспроизводиться определенные периодические объявления в случайном порядке. См. periodic-announce.
relative-periodic-announce yes,no Если задано значение yes, таймер periodic-announce-frequency будет запускаться при достижении конца воспроизводимого файла, а не с самого начала. По умолчанию no
announce-holdtime yes,no,once Определяет, должно ли расчетное время ожидания воспроизводиться вместе с периодическими объявлениями. Можно установить значение yes, no или once (только один раз).
announce-position yes,no,limit,more Определяет, должна ли быть объявлена позиция вызывающего абонента в очереди. Если no — положение не будет объявлено. Если задано значение yes, позиция вызывающего абонента всегда будет объявлена. Если установлено значение limit, абонент будет слышать свое положение в очереди, только если оно находится в пределах лимита, определенного announce-position-limit. Если значение больше, то абонент будет слышать его позицию, только если он не входит в число определяемых announce-position-limit.
announce-position-limit Число от нуля и выше Используется, если вы определили announce-position как limit или more.
announce-round-seconds Значение в секундах Если это значение не равно нулю, объявляется также и число секунд, округленное до заданного значения.

Таблица 13-8 показывает файлы, которые будут использоваться при воспроизведении объявлений для абонента.

Таблица 13-8. Опции для управления воспроизведением подсказок в очереди

Опция Возможные значения Описание
musicclass Класс музыки определенный в musiconhold.conf Устанавливает класс музыки, который будет использоваться определенной очередью. Вы также можете переопределить это значение с помощью канальной переменной CHANNEL(musicclass).
queue-thankyou Имя файла подсказки для воспроизведения Если не определено, воспроизводится значение по умолчанию («Благодарим за терпение»). Если установлено пустое значение, запрос не будет воспроизводиться вообще.
queue-youarenext Имя файла подсказки для воспроизведения Если не определено, воспроизводится значение по умолчанию («Ваш звонок является первым в очереди и будет отвечен первым доступным оператором»). Если установлено пустое значение, запрос не будет воспроизводиться вообще.
queue-thereare Имя файла подсказки для воспроизведения Если не определено, воспроизводится значение по умолчанию («Ваша позиция в очереди»). Если установлено пустое значение, запрос не будет воспроизводиться вообще.
queue — callswaiting Имя файла подсказки для воспроизведения Если не определено, воспроизводится значение по умолчанию («Ожидайте, пожалуйста, ответа оператора»). Если установлено пустое значение, запрос не будет воспроизводиться вообще.
queue-holdtime Имя файла подсказки для воспроизведения Если не определено, воспроизводится значение по умолчанию («Прогнозируемое время ожидания составляет»).Если установлено пустое значение, запрос не будет воспроизводиться вообще.
queue-minutes Имя файла подсказки для воспроизведения Если не определено, воспроизводится значение по умолчанию («минут»). Если установлено пустое значение, запрос не будет воспроизводиться вообще.
queue-seconds Имя файла подсказки для воспроизведения Если не определено, воспроизводится значение по умолчанию («секунд»). Если установлено пустое значение, запрос не будет воспроизводиться вообще.
queue-reporthold Имя файла подсказки для воспроизведения Если не определено, воспроизводится значение по умолчанию («Время ожидания»). Если установлено пустое значение, запрос не будет воспроизводиться вообще.
periodic-announce Набор периодических объявлений для воспроизведения, разделенных запятыми Подсказки воспроизводятся в порядке их определения. По умолчанию очередь-периодическое-объявление («Все наши операторы заняты, пожалуйста, оставайтесь на линии и Ваш звонок будет обслужен первым доступным оператором»).

Если количество параметров, предназначенных для воспроизведения объявлений для вызывающих абонентов, является показателем их важности, вероятно, в ваших же интересах использовать их в полной мере. Варианты в Таблице 13-7помогут вам определить, когда воспроизводить объявления для абонентов, а варианты в Таблице 13-8 помогут контролировать то, что мы воспроизводим для наших абонентов. Имея эти таблицы давайте рассмотрим пример очереди, в которой мы определили некоторые значения. Мы будем использовать наш базовый шаблон очереди в качестве отправной точки:

[general]

autofill=yes ; распределить всех ожидающих абонентов среди доступных

; агентов

shared_lastcall=yes ; уважайте время завершения для участников, вошедших в

; более чем одну очередь

[StandardQueue](!) ; шаблон для обеспечения общих функций

musicclass=default ; воспроизводить музыку [default]

strategy=rrmemory ; использовать стратегию Round Robin Memory

joinempty=yes ; не присоединяться к очереди, когда нет доступных

; участников

leavewhenempty=no ; покинуть очередь, когда нет доступных участников

ringinuse=no ; не звонить участникам, когда он InUse (предотвращает

; многократные звонки оператору)

[sales]

(StandardQueue) ; создать очередь sales используя параметры из шаблона

; StandardQueue

[support]

(StandardQueue); создать очередь support используя параметры из шаблона

; StandardQueue

Теперь мы изменим шаблон StardardQueue для управления нашими объявлениями:

[general]

autofill=yes ; распределить всех ожидающих абонентов среди доступных участников

shared_lastcall=yes ; уважайте время завершения для участников, вошедших в

; более чем одну очередь

[StandardQueue](!) ; шаблон для обеспечения общих функций

musicclass=default ; воспроизводить музыку [default]

strategy=rrmemory ; использовать стратегию Round Robin Memory

joinempty=yes ; не присоединяться к очереди, когда нет доступных

; участников

leavewhenempty=no ; покинуть очередь, когда нет доступных участников

ringinuse=no ; не звонить участникам, когда он InUse (предотвращает

; многократные звонки оператору)

; ——— Управление объявлениями ———
announce-frequency=30 ; объявляет время удержания и позицию

; звонящего каждые 30 секунд

min-announce-frequency=30 ; минимальное количество времени, которое ; должно пройти, прежде чем позиция

; вызывающего абонента будет объявлена

periodic-announce-frequency=45 ; определяет, как часто следует

; воспроизводить периодическое объявление

; для звонящего

random-periodic-announce=no ; определяет, следует ли воспроизводить

; периодические объявления в случайном

; порядке или по порядку

relative-periodic-announce=yes ; определяет, начинается ли таймер в

; конце воспроизведение файла (yes) или

; начале (no)

announce-holdtime=once ; определяет, должно ли предполагаемое

; время удержания играться вместе с ; периодическим объявлением

announce-position=limit ; определяет, должны ли мы объявить

; позицию звонящего в очереди

announce-position-limit=10 ; определяет предельное значение, в

; пределах которого мы объявляем позицию

; абонента (когда announce-position

; установлено на limit или more)

announce-round-seconds=30 ; округляет объявление времени удержания

; до ближайшего 30-секундного значения

Давайте опишем, что мы только что установили в нашем шаблоне StandardQueue.

Мы будем сообщать время удержания и позицию вызывающего абонента каждые 30 секунд (announce-frequency)8 и убедитесь что минимальное количество времени, которое проходит до того, как мы объявим его снова, составляет не менее 30 секунд (min-announce-frequency).9 Нам нужно ограничить частоту воспроизведения наших объявлений для вызывающих абонентов чтобы не раздражать их. Периодически мы будем воспроизводить звонящим объявление, в котором поблагодарим их за ожидание10, и уверим что агент будет с ними в ближайшее время. (Объявление определяется настройкой periodic-announcement. Мы используем объявление по умолчанию, но вы можете определить одно или несколько объявлений самостоятельно, используя periodic-announce.)

Эти периодические объявления будут воспроизводиться каждые 45 секунд (periodic-announce-frequency) в порядке их определения (random-periodic-announce). Чтобы определить когда таймер periodic-announce-frequency должен запуститься, мы используем relative-periodic-announce. Настройка yes означает, что таймер запустится после окончания объявления, а не в начале. Проблема, с которой вы можете столкнуться, заключается в том, что если вы установите для него значение no, то если ваше периодическое объявление запускается в течение какого-либо значительного промежутка времени, например 30 секунд, то будет выглядеть так, будто оно играет каждые 15 секунд, а не 45, как на это может быть расчитано. Кроме того, если вы нашли какую-то полуприличную музыку на удержание, и ваши абоненты наслаждаются ею, прерывание для воспроизведения еще одного сообщения может привести к тому, что у них действительно закипит кровь. Когда им наконец ответят, ваши бедные агенты получат основную тяжесть их гнева, хотя на самом деле это ваша вина.11

Сколько раз мы объявляем время удержания для вызывающего абонента, контролируется с помощью параметра announce-holdtime, значение которого мы установили once. Установка значения yes будет объявлять время каждый раз, а установка на no отключит его.

Мы настраиваем, как и когда объявлять расчетное оставшееся время удержания звонящего через announce-position, который мы установили для ограничения. Использование значения limit для announce-position позволяет нам объявлять позицию вызывающего абонента, только если она находится в пределах, определенных в announce-position-limit. Таким образом, в этом случае мы объявляем позиции абонентов только если они находятся в первых 10 позициях очереди. Мы также могли бы использовать yes, чтобы объявлять позицию каждый раз, когда воспроизводится периодическое объявление; установив для нее значение no мы укажем никогда не объявлять ее или используя значение more укажем объявлять позицию только тогда, когда она превышает значение, установленное для announce-position-limit.

Наш последний параметр announce-round-seconds контролирует значение, к которому округляется объявляемое время удержания вызывающего абонента. В этом случае вместо того, чтобы сказать «1 минута и 23 секунды», значение будет округлено до ближайшего 30-секундного значения, что приведет к приглашению «1 минута и 30 секунд».

Переполнение

Переполнение очереди выполняется либо со значением тайм-аута, либо когда нет доступных участников очереди (как определено joinempty или leftwhenempty). В этом разделе мы обсудим, как контролировать переполнение.

Управление тайм-аутами

Приложение Queue() поддерживает два вида тайм-аутов: один для максимального периода времени, в течение которого вызывающий абонент находится в очереди, а другой — как долго звонить устройству при попытке соединить вызывающего абонента с участником очереди. Они не связаны, но могут влиять друг на друга. В этом разделе мы поговорим о максимальном периоде времени, в течение которого вызывающий абонент остается в приложении Queue() до того, как вызов переполнится, до следующего шага в диалплане, который может быть чем-то вроде VoiceMail() или даже другой очередью. После того, как вызов выпал из очереди, он может отправиться куда угодно, куда обычно может идти вызов, управляемый диалпланом.

Таймауты задаются в двух местах. Время ожидания, указывающее, как долго звонить участникам очереди, указывается в файле queues.conf. Абсолютное время ожидания (как долго абонент остается в очереди) управляется через приложение Queue(). Чтобы задать максимальное время пребывания абонентов в очереди, просто укажите его после имени очереди в приложении Queue():

[Queues]

exten => 7000,1,Verbose(2,Joining the support queue for a maximum of 2 minutes)

same => n,Queue(support,120)

same => n,VoiceMail(support@queues,u)

same => n,Hangup()

Конечно, мы можем определить другое назначение, но приложение VoiceMail() является общим местом назначения переполнения для очереди. Очевидно, что отправка звонков на голосовую почту не идеальна (они надеялись поговорить с кем-то вживую) поэтому убедитесь, что кто-то регулярно проверяет её и перезванивает вашим клиентам.

Теперь предположим, что мы установили наше абсолютное время ожидания равным 10 секундам, наше значение времени ожидания для звонков участникам очереди равным 5 секундам, а наше значение времени ожидания повторных попыток — 4 секунды. В этом случае мы будем звонить участнику очереди в течение 5 секунд, а затем ждать 4 секунды, прежде чем пытаться набрать другому участнику очереди. Это дает нам до 9 секунд нашего абсолютного тайм-аута в 10 секунд. В этот момент мы должны позвонить второму участнику очереди на 1 секунду и затем выйти из очереди, или мы должны позвонить этому участнику в течение полных 5 секунд перед выходом?

Мы контролируем, какое значение тайм-аута имеет приоритет с помощью параметра в timeoutpriority в queues.conf. Доступные значения app и conf. Если мы хотим, чтобы тайм-аут приложения (абсолютный тайм-аут) имел приоритет, что приведет к тому, что наш вызывающий будет выгнан через ровно 10 секунд, мы должны установить значение timeoutpriority в app. Если мы хотим, чтобы таймаут файла конфигурации имел приоритет и закончил звонить участнику очереди, что заставит вызывающего абонента оставаться в очереди немного дольше, мы должны установить timeoutpriority в conf. Значением по умолчанию является app (это поведение по умолчанию из предыдущих версий Asterisk).

Контроль времени присоединения и выхода из очереди

Asterisk предоставляет две опции, которые контролируют, когда вызывающие абоненты могут присоединиться и вынуждены покинуть очередь, в зависимости от статусов участников очереди. Первая опция — joinempty, используется для контроля могут ли абоненты входить в очередь. Опция leftwhenempty используется для управления тем, когда вызывающие абоненты, уже находящиеся в очереди, должны быть удалены из этой очереди (то есть, если все участники очереди становятся недоступными). Обе опции принимают список значений через запятую, которые управляют этим поведением. Факторы перечислены в Таблице 13-9.

Таблица 13-9. Опции, которые могут быть установлены для joinempty или leavewhenempty

Значение Описание
paused Пользователи считаются недоступными, если они приостановлены.
penalty Участники считаются недоступными, если их штрафы меньше QUEUE_MAX_PENALTY.
inuse Участники считаются недоступными, если их состояние устройства In Use.
ringing Участники считаются недоступными, если их статус устройства Ringing.
unavailable Относится в первую очередь к агентским каналам; если агент не вошел в систему, но является участником очереди, канал считается недоступным.
invalid Участники считаются недоступными, если статус их устройства Invalid. Обычно это условие ошибки.
unknown Участники считаются недоступными, если статус устройства неизвестен.
wrapup Участники считаются недоступными, если они в настоящее время находятся во времени отдыха после завершения вызова.

Для joinempty прежде чем поместить вызывающего абонента в очередь, все участники проверяются на доступностьс помощью перечисленных в качестве критериев факторов. Если все участники считаются недоступными, абоненту не будет разрешен вход в очередь, и диалплан будет продолжен со следующего приоритета.12 Для опции leavewhenempty статусы участников периодически проверяются условиями, перечисленными выше; если будет установлено, что участники готовы принять звонки, абонент удаляется из очереди, выполнение диалплана продолжится со следующего приоритета.

Примером использования joinempty может быть:

joinempty=paused,inuse,invalid

При такой конфигурации, до входа абонента в очередь, статусы всех участников очереди будут проверены и вызывающему абоненту не будет разрешено войти в очередь, если не будет найден по крайней мере один участник очереди не имеющий статуса paused,inuse,invalid.

Примером leavewhenempty может быть что-то вроде:

leavewhenempty=inuse,ringing

В этом случае статусы участников очереди будут периодически проверяться, а вызывающие абоненты будут удаляться из очереди, если не будет найдено ни одного участника очереди, без статуса inuse или ringing.

Предыдущие версии Asterisk использовали значения yes, no, strict и loose в качестве назначаемых значений. Сопоставление этих значений показано в Таблице 13-10.

Таблица 13-10. Сопоставление старых и новых значений для контроля входа и выхода абонентов из очередей

Значение Сопоставление (joinempty) Сопоставление (leavewhenempty )
yes (пусто) penalty,paused,invalid
no penalty,paused,invalid (пусто)
strict penalty,paused,invalid,unavailable penalty,paused,invalid,unavailable
loose penalty,invalid penalty,invalid

Использование локальных (Local) каналов

Использование локальных каналов в качестве участников очереди является мощным способом выполнения кода диалплана перед набором фактического номера агента. Например, он позволяет нам определять пользовательские переменные канала, записывать в файл журнала, устанавливать некоторое ограничение на длину вызова (например, если это платная услуга), отправлять сообщения всех видов повсеместно, выполнять транзакции базы данных и многие другие действия, которые мы могли бы выполнить в тот момент, когда приложение Queue() решило предоставить вызывающего абонента конкретному участнику (включая возврат Congestion() что приведет к возврату абонента в очередь, поскольку очередь не будет считать этот вызов успешно доставленным агенту).

При использовании локальных каналов для очередей они добавляются точно так же, как и любые другие каналы, как правило, либо определяя их в файле queues.conf, либо добавляя их динамически через приложение AddQueueMember().

Если вы хотите определить их в файле queues.conf, добавление локального канала будет выглядеть примерно так:

; queues.conf

[support](StandardQueue)

member => Local/SIP-0000FFFF0001@MemberConnector ; передайте технологию

; дозвона и идентификатор устройства,

; разделенные дефисом. Мы разобьем его

; внутри контекста MemberConnector.

Обратите внимание, как мы передали тип технологии, которую хотим вызвать вместе с идентификатором устройства, в контекст MemberConnector. Мы просто использовали дефис (хотя мы могли бы использовать почти все в качестве аргумента-разделителя) в качестве маркера поля. Мы будем использовать функцию CUT() внутри контекста MemberConnector и назначим первое поле (SIP) одному каналу, переменную и второе поле (0000FFFF0001) — другой переменной канала, которая затем будет использоваться для вызова конечной точки.

Передача информации для последующего «разбиения» в контексте, используемом локальным каналом, является распространенным и полезным методом (по типу функции explode() в PHP).

Поскольку наш локальный канал ссылается на контекст MemberConnector, нам понадобится этот контекст в нашем диалплане для того, чтобы он содержал сведения, необходимые для фактического подключения вызывающего абонента к физическому каналу, который использует агент (в идеале какой-нибудь SIP-телефон):

[MemberConnector]

exten => _[A-Za-z0-9].,1,Verbose(2,Connect ${CALLERID(all)} to Agent at ${EXTEN})

; фильтруем любые неверные символы, допустимы только цифры, буквы и дефис

same => n,Set(QueueMember=${FILTER(A-Za-z0-9\-,${EXTEN})})

; назначить первое поле QueueMember — Technology; дефис как разделитель

same => n,Set(Technology=${CUT(QueueMember,-,1)})

; назначить второе поле QueueMember — Device используя дефис как разделитель

same => n,Set(Device=${CUT(QueueMember,-,2)})

; вызываем агента

same => n,Dial(${Technology}/${Device})

same => n,Hangup()

Теперь у нас есть весь код, необходимый для передачи вызова очереди оператору через локальный канал. Однако, поскольку участник очереди является локальным каналом, а не каналом SIP, Queue() не обязательно будет знать состояние, в котором находится вызов, особенно когда локальный канал оптимизирован вне пути [см. Asterisk wiki для получения информации о модификаторе /n, который приводит к тому, что локальный канал не будет оптимизирован вне пути]. Очередь будет контролировать состояние локального канала, а не устройства, которое мы действительно хотим отслеживать; таким образом, фактическое состояние канала, который использует агент, не будет известно очереди.

К счастью, мы можем предоставить Queue() реальное физическое устройство для мониторинга и связать его с локальным каналом, чтобы состояние участника очереди всегда соответствовало состоянию устройства, которое мы в конечном итоге вызываем. Наш участник очереди должен быть изменен в файле queues.conf следующим образом:

; queues.conf

[support](StandardQueue)

member => Local/SIP-0000FFFF0001@MemberConnector,,,SIP/0000FFFF0001

Только SIP-каналы способны отправлять обратно достоверную информацию о состоянии устройства, поэтому настоятельно рекомендуется использовать только эти каналы для локальных каналов в качестве участников очереди или, по крайней мере, знать, что информация о состоянии может быть ненадежной для других типов канальных технологий (таких как DAHDI, IAX2 и т.д.).

Помимо определения участников в файле queues.conf, вы также можете использовать приложения AddQueueMember() и RemoveQueueMember() для добавления и удаления локального канала участника из очереди, используя код диалплана (как вы можете с любым типом канала). AddQueueMember() также имеет возможность устанавливать состояния интерфейса, аналогично тому, что мы определили статически в файле queues.conf. Ниже приведен пример того, как вы можете это сделать:

[QueueMemberLogin]

exten => 500,1,Verbose(2,Logging in device ${CHANNEL(peername)} into the support queue)

; Сохраняем технологию устройства в переменную канала MemberTech

same => n,Set(MemberTech=${CHANNEL(channeltype)})

; Сохраняем идентификатор устройства в переменную канала MemberIdent

same => n,Set(MemberIdent=${CHANNEL(peername)})

; Создаем имя интерфейса из типа канала и имени пира

; и назначаем его переменной канала Interface

same => n,Set(Interface=${MemberTech}/${MemberIdent})

; Добавляем участника в очередь support, используя канал Local. Мы

; используем тот же формат, что и раньше, разделяя технологию и

; идентификатор устройства дефисом и передавая эту информацию в контекст

; MemberConnector.

; Затем мы используем функцию IF(), чтобы определить, является ли

; технология участника SIP и если да, то передаем содержимое переменной

; канала Interface в качестве значения в поле интерфейса состояния

; приложения AddQueueMember().

;

; При отладке может потребоваться жесткий код AddQueueMember c

; одним указанным интерфейсом, чтобы получить представление о синтаксисе,

; например:

; **** В этой строке не должно быть разрывов

same => n,AddQueueMember(support,Local/SIP-0000FFFF0001

@MemberConnector,,,SIP/0000FFFF0001)

; однако ваш диалплан должен быть закодирован для обработки всех каналов и

; участников, как здесь:

; **** В этой строке не должно быть разрывов

same => n,AddQueueMember(support,Local/${MemberTech}-${MemberIdent}

@MemberConnector,,,${IF($[${MemberTech} = SIP]?${Interface})})

same => n,Playback(silence/1)

; Воспроизведение файла agent-loginok или agent-incorrect в зависимости от

; значения переменной AQMSTATUS.

same => n,Playback(${IF($[${AQMSTATUS} = ADDED]?agent-loginok:agent-incorrect)})

same => n,Hangup()

Теперь, когда мы можем добавлять устройства в очередь, используя каналы Local, давайте посмотрим где это может быть полезно. В нашем примере мы собираемся контролировать количество вызовов на не-SIP-каналы или SIP-устройства с более чем одной линией (что типично для стандартного SIP-телефона). Мы можем использовать функции GROUP() и GROUP_COUNT() для отслеживания количества вызовов к конечной точке. Мы изменим наш контекст MemberConnector, чтобы принять это во внимание:

[MemberConnector]

exten => _[A-Za-z0-9].,1,Verbose(2,Connect ${CALLERID(all)} to Agent at ${EXTEN})

; фильтруем любые неверные символы, допустимы только цифры, буквы и дефис

same => n,Set(QueueMember=${FILTER(A-Za-z0-9\-,${EXTEN})})

; назначить первое поле QueueMember — Technology; дефис как разделитель

same => n,Set(Technology=${CUT(QueueMember,-,1)})

; назначить второе поле QueueMember — Device используя дефис как разделитель

same => n,Set(Device=${CUT(QueueMember,-,2)})

; Увеличить значение группы внутри категории queue_members на 1

same => n,Set(GROUP(queue_members)=${Technology}-${Device})

; Проверить если group@category больше 1, то вернуть Congestion()

; (слишком много каналов)

;

; **** В этой строке не должно быть разрывов

same => n,ExecIf($[${GROUP_COUNT(${Technology}-${Device}@queue_members)} > 1]?Congestion())

; вызываем агента

same => n,Dial(${Technology}/${Device})

same => n,Hangup()

Возврат Congestion() приведет к тому, что вызывающий будет вернут в очередь (пока это происходит) звонящий не получает никаких признаков того, что что-то не так, и продолжает слушать музыку, пока на его звонок не ответит какой-то канал13). В идеале ваша очередь запрограммирована на попытку вызова другого агента; однако вы должны иметь в виду, что если app_queue определяет, что этот участник по-прежнему является первым для вызова, то вызов просто будет повторно направлен к тому же агенту (и снова получит перегрузку, и, таким образом, потенциально может создать цикл с перегрузкой ЦП). Чтобы избежать этого, вам нужно убедиться, что ваша очередь использует стратегию распределения, такую как round_robin, random или любую другую, которая гарантирует, что один и тот же участник не будет пробоваться снова и снова.

Мы также использовали этот метод для создания типа процесса бронирования. Если вы хотите вызвать агента напрямую (например, если вызывающему абоненту необходимо связаться с конкретным агентом), вы можете зарезервировать этого агента с помощью функций GROUP() и GROUP_COUNT(), чтобы приостановить работу агента в очереди до тех пор, пока вызывающий не сможет подключиться. Это особенно полезно в ситуациях, когда вам необходимо воспроизвести какое-либо объявление для абонента до его соединения с агентом, но не хотите, чтобы агент подключался к другому вызывающему абоненту во время воспроизведения объявления.

Использование каналов Local для каналов участников не облегчит проектирование и отладку очереди, но даст вам гораздо больше возможностей для ваших очередей, чем простое использование app_queue, поэтому, если у вас сложные требования к очереди, использование каналов Local может дать вам уровень контроля, который вы иначе и не могли бы не иметь.

Статистика очереди: файл queue_log

Файл queue_log (обычно расположенный в /var/log/asterisk) содержит накопительную информацию о событиях очередей, определенных в вашей системе (когда очередь перезагружается, когда добавляются участники очереди или удаляются и т.д.), а также некоторые детали вызова (например, их статус и к каким каналам были подключены абоненты). Журнал очереди включен по умолчанию, но им можно управлять через файл logger.conf. Существует три параметра, относящихся к файлу queue_log, а именно:

queue_log

Включен ли журнал очереди или нет. Допустимые значения: yes или no (по умолчанию yes).

queue_log_to_file

Определяет, должен ли журнал очереди записываться в файл, даже если присутствует серверная часть Realtime. Допустимые значения: yes или no (по умолчанию — no).

queue_log_name

Управляет именем журнала очереди. По умолчанию это queue_log.

Журнал очереди представляет собой разделенный на каналы список событий. Поля в файле queue_log следующие:

  • Отметка времени события
  • Уникальный идентификатор вызова
  • Имя очереди
  • Наименование мостового канала
  • Тип события
  • Ноль или более параметров события

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

1292281046|psy1-1292281041.87|7100|NONE|ENTERQUEUE||4165551212|1

1292281046|psy1-1292281041.87|7100|Local/9996@MemberConnector|RINGNOANSWER|0

1292281048|psy1-1292281041.87|7100|Local/9990@MemberConnector|CONNECT|2

|psy1-1292281046.90|0

1292284121|psy1-1292281041.87|7100|Local/9990@MemberCo|COMPLETECALLER|2|3073|1

1292284222|MANAGER|7100|Local/9990@MemberConnector|REMOVEMEMBER|

1292284222|MANAGER|7200|Local/9990@MemberConnector|REMOVEMEMBER|

1292284491|MANAGER|7100|Local/9990@MemberConnector|ADDMEMBER|

1292284491|MANAGER|7200|Local/9990@MemberConnector|ADDMEMBER|

1292284519|psy1-1292284515.93|7100|NONE|ENTERQUEUE||4165551212|1

1292284519|psy1-1292284515.93|7100|Local/9996@MemberConnector|RINGNOANSWER|0

1292284521|psy1-1292284515.93|7100|Local/9990@MemberConnector|CONNECT|2

|psy1-1292284519.96|0

1292284552|MANAGER|7100|Local/9990@MemberConnector|REMOVEMEMBER|

1292284552|MANAGER|7200|Local/9990@MemberConnector|REMOVEMEMBER|

1292284562|psy1-1292284515.93|7100|Local/9990@MemberCo|COMPLETECALLER|2|41|1

Как видно из этого примера, не всегда может быть уникальный идентификатор для события. Внешние сервисы, такие как Asterisk Manager Interface (AMI), могут выполнять действия в очереди и в этих случаях вы увидите что-то вроде MANAGER в поле Уникальный идентификатор.

Доступные события и информация, которую они предоставляют, описаны в Таблице 13-11.

Таблица 13-11. События в журнале очереди Asterisk

Событие Предоставляемая информация
ABANDON Записывается, когда абонент в очереди вешает трубку, прежде чем на его вызов отвечает агент. Три параметра предназначены для отказа: позиция звонящего на момент отбоя, исходное положение абонента при поступлении в очередь, и количество времени, которое абонент ожидал до отбоя.
ADDMEMBER Записывается при добавлении участника в очередь. Имя канала моста будет заполнено именем канала, добавленного в очередь.
AGENTDUMP Указывает, что агент сбросил вызов абонента во время воспроизведения объявления очереди, до того как они были соединены вместе.
AGENTLOGIN Записывается при входе агента в систему. Поле канала моста будет содержать что-то вроде Agent/9994 при входе с помощью chan_agent, а первое поле параметра будет содержать вход канала (например, SIP/0000FFFF0001).
AGENTLOGOFF Регистрируется при выходе агента из системы вместе с параметром, указывающим, как долго агент был в системе. Обратите внимание, что поскольку RemoveQueueMember() часто используется для выхода агента из системы, этот параметр может не записываться. Смотрите REMOVEMEMBER вместо этого.
COMPLETEAGENT Записывается, когда вызов соединен мостом с агентом, и агент вешает трубку, наряду с параметрами, указывающими количество времени, в течение которого абонент удерживался в очереди, длину вызова с агентом и исходной позицией, с которой абонент вошел в очередь.
COMPLETECALLER То же самое, что и COMPLETEAGENT, за исключением того, что звонивший повесил трубку, а не агент.
CONFIGURELOAD Указывает, что конфигурация очереди была перезагружена (например, через module reload app_queue.so).
CONNECT Записывается когда абонент и агент были соединены. Также записываются три параметра: время ожидания абонента в очереди, уникальный идентификатор канала участника очереди, к которому абонент был присоединен, и время, в течении которого звонил телефон участника очереди до того, как он ответил.
ENTERQUEUE Записывается, когда абонент входит в очередь. Также записываются два параметра: URL (если указан) и CallerID абонента.
EXITEMPTY Записывается, когда абонент удаляется из очереди из-за отсутствия доступных агентов для ответа на вызов (как указано в параметре leavewhenempty). Также записываются три параметра: позиция абонента в очереди, исходная позиция, c которой абонент вошел в очередь, и время нахождения абонента в очереди.
EXITWITHKEY Записывается, когда абонент выходит из очереди, нажав одну клавишу DTMF на своем телефоне, чтобы выйти из очереди и продолжить в диалплане (как разрешено параметром context в queues.conf). Записываются четыре параметра: клавиша, используемая для выхода из очереди, позиция абонента в очереди при выходе, исходная позиция абонента с которой он поступил в очередь и время ожидания абонентом в очереди.
EXITWITHTIMEOUT Записывается, когда абонент удаляется из очереди из-за тайм-аута (как указано в параметре timeout для Queue()). Записываются также три параметра: положение абонента при выходе из очереди, исходное положение абонента при входе в очередь и время ожидания абонента в очереди.
PAUSE Записывается при приостановке участника очереди.
PAUSEALL Записывается при приостановке всех участников очереди.
UNPAUSE Записывается при возобновлении участника очереди.
UNPAUSEALL Записывается при возобновлении всех участников очереди.
PENALTY Записывается при изменении штрафа участника. Штраф можно изменить несколькими способами, такими как функция QUEUE_MEMBER_PENALTY(), AMI или Asterisk CLI.
REMOVEMEMBER Записывается, когда участник очереди удаляется из очереди. Поле канал моста будет содержать имя участника, удаленного из очереди.
RINGNOANSWER Регистрируется, когда участник очереди вызывается в течение определенного периода времени и превышено значение времени ожидания для вызова участника очереди. Также будет записан один параметр, указывающий время, в течение которого звонил добавочный номер участника.
TRANSFER Записывается при переводе абонента на другой внутренний номер. Дополнительные параметры, которые также записываются: расширение и контекст, в который была переведен абонент, время удержания абонента в очереди, количество времени, в течении которого абонент разговаривал с участником очереди, и исходное положение абонента, когда он вошел в очередь.a
SYSCOMPAT Записывается, если агент пытается ответить на вызов, но вызов не может быть настроен из-за несовместимости в настройке медианосителя.

a Обратите внимание, что при передаче вызывающего абонента с использованием SIP-трансфера (а не встроенного трансфера, запускаемого через DTMF и настраиваемого в файле functions.conf), событие TRANSFER может не записываться.

Вывод

Мы начали эту главу с рассмотрения простых очередей вызовов, обсуждения того, что они из себя представляют, как они работают и когда вы, возможно, захотите их использовать. После построения простой очереди мы изучили как управлять участниками очереди с помощью различных средств (включая использование каналов Local, которые обеспечивают возможность выполнения некоторой логики диалплана непосредственно перед подключением к участнику очереди). Мы также изучили все доступные нам опции в файлах queues.conf, agents.conf и queuerules.conf, которые предоставляют нам детальный контроль над любыми настраиваемыми очередями. Конечно, нам нужна возможность отслеживать, что делают наши очереди, поэтому мы быстро просмотрели файл журнала очереди и различные поля, записанные в результате событий, происходящих в наших очередях.

Благодаря знаниям, приведенным в этой главе, вы должны быть на пути к реализации успешного набора очередей для вашей компании.

1Это-распространенное заблуждение, что очередь может позволить вам обрабатывать больше вызовов. Это не совсем верно: ваши абоненты все равно захотят поговорить с живым человеком, и они будут готовы ждать так долго. Другими словами, если у вас не хватает персонала, ваша очередь может оказаться не более чем препятствием для ваших абонентов. Это то же самое, будь вы на телефоне или на кассе WalMart. Никто не любит стоять в очереди. Идеальная очередь невидима для абонентов, так как на их звонки отвечают немедленно, без необходимости ожидания.

2Существует несколько книг, в которых обсуждаются показатели центра обработки вызовов и доступные стратегии очередей, такие как James C. Abbott’s The Executive Guide to Call Center Metrics (Robert Houston Smith).

3В оригинале queue members, что можно перевести как члены очереди, но я выбрал вариант участники (прим. переводчика).

4Время отдыха (или время подведения итогов) используется для агентов, которым может потребоваться выполнить какую-либо регистрацию в журнал или другую функцию после завершения вызова. Это дает им льготный период в несколько секунд, чтобы выполнить эту задачу, прежде чем принимать другой вызов.

5Мы будем использовать символ ^ как разделитель. Вероятно, вы могли бы использовать другой символ, только если он не является парсером Asterisk, который будет выглядеть как обычный разделитель (и, таким образом, запутать вас). Поэтому избегайте запятых, точек с запятой и т.д.

6В некоторых случаях может быть полезно разрешить очереди предоставлять несколько вызовов участнику, но в целом человек может обрабатывать только один вызов за раз.

7 Подобно добавлению балласта жокею или гоночному автомобилю

8Позиция и время удержания абонентов объявляются только в том случае, если в очереди находится более одного человека.

9Обратите внимание, что в продакшене может потребоваться установить значение более 30 секунд, особенно если среднее время удержания таково, что абонент может услышать это приглашение более одного раза во время удержания. Установите такое значение, чтобы большинство абонентов никогда не слышали это приглашение более двух раз, прежде чем им ответят.

10Спасибо им, но не извиняйтесь, так как это звучит просто неискренне, так как по сути они извиняются перед машиной, и они это знают. Основная цель этих подсказок состоит в том, чтобы помочь абоненту понять, что система все еще держит их в очереди.

11Просто говорю

12 Если приоритет n + 1 (из которого вызывалось приложение Queue()) не определен, вызов будет прерван. Другими словами, не используйте эту функцию, если ваш диалплан не делает что-то полезное на шаге, следующем сразу за Queue().

13Очевидно, что в локальном канале не стоит использовать код диалплана, который будет отвечать на вызов, например Answer(), Playback() и так далее.

Остались вопросы?

Я - Виталий Шелест, менеджер компании Voxlink. Хотите уточнить детали или готовы оставить заявку? Укажите номер телефона, я перезвоню в течение 3-х секунд.

VoIP оборудование


ближайшие курсы

10 доводов в пользу Asterisk

Распространяется бесплатно.

Asterisk – программное обеспечение с открытым исходным кодом, распространяется по лицензии GPL. Следовательно, установив один раз Asterisk вам не придется дополнительно платить за новых абонентов, подключение новых транков, расширение функционала и прочие лицензии. Это приближает стоимость владения станцией к нулю.

Безопасен в использовании.

Любое программное обеспечение может стать объектом интереса злоумышленников, в том числе телефонная станция. Однако, сам Asterisk, а также операционная система, на которой он работает, дают множество инструментов защиты от любых атак. При грамотной настройке безопасности у злоумышленников нет никаких шансов попасть на станцию.

Надежен в эксплуатации.

Время работы серверов некоторых наших клиентов исчисляется годами. Это значит, что Asterisk работает несколько лет, ему не требуются никакие перезагрузки или принудительные отключения. А еще это говорит о том, что в районе отличная ситуация с электроэнергией, но это уже не заслуга Asterisk.

Гибкий в настройке.

Зачастую возможности Asterisk ограничивает только фантазия пользователя. Ни один конструктор шаблонов не сравнится с Asterisk по гибкости настройки. Это позволяет решать с помощью Asterisk любые бизнес задачи, даже те, в которых выбор в его пользу не кажется изначально очевидным.

Имеет огромный функционал.

Во многом именно Asterisk показал какой должна быть современная телефонная станция. За многие годы развития функциональность Asterisk расширилась, а все основные возможности по-прежнему доступны бесплатно сразу после установки.

Интегрируется с любыми системами.

То, что Asterisk не умеет сам, он позволяет реализовать за счет интеграции. Это могут быть интеграции с коммерческими телефонными станциями, CRM, ERP системами, биллингом, сервисами колл-трекинга, колл-бэка и модулями статистики и аналитики.

Позволяет телефонизировать офис за считанные часы.

В нашей практике были проекты, реализованные за один рабочий день. Это значит, что утром к нам обращался клиент, а уже через несколько часов он пользовался новой IP-АТС. Безусловно, такая скорость редкость, ведь АТС – инструмент зарабатывания денег для многих компаний и спешка во внедрении не уместна. Но в случае острой необходимости Asterisk готов к быстрому старту.

Отличная масштабируемость.

Очень утомительно постоянно возвращаться к одному и тому же вопросу. Такое часто бывает в случае некачественного исполнения работ или выбора заведомо неподходящего бизнес-решения. С Asterisk точно не будет такой проблемы! Телефонная станция, построенная на Asterisk может быть масштабируема до немыслимых размеров. Главное – правильно подобрать оборудование.

Повышает управляемость бизнеса.

Asterisk дает не просто набор полезных функций, он повышает управляемость организации, качества и комфортности управления, а также увеличивает прозрачность бизнеса для руководства. Достичь этого можно, например, за счет автоматизации отчетов, подключения бота в Telegram, санкционированного доступа к станции из любой точки мира.

Снижает расходы на связь.

Связь между внутренними абонентами IP-АТС бесплатна всегда, независимо от их географического расположения. Также к Asterisk можно подключить любых операторов телефонии, в том числе GSM сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.