28 июня 2011 в 15:41

Голосовое дерево на Asterisk своими руками из песочницы

image

Доброго времени суток хабражители.

Хотелось бы поделиться опытом развертывания голосовых деревьев (IVR), с помощью Asterisk.
Для этого нам понадобится:
  • Машина с установленным Asterisk`ом
  • Телефонный номер, заведенный в Asterisk посредством SIP/H.323 от провайдера ip-телефонии, либо посредством аналоговой линии/цифрового потока Е1 через платы Digium

Ниже я опишу как создать простое голосовое дерево. Главное от чего необходимо отталкиваться — это структура дерева.
Типичное дерево выглядит примерно так:
  • Здравствуйте вы позвонили в %companyname%
  • Для того, чтобы соединится с отделом 1 нажмите 1
  • Для того, чтобы соединится с отделом 2 нажмите 2
  • Чтобы отправить факс нажмите 9
  • Если вы знаете внутренний номер абонента, наберите его в тональном режиме или дождитесь ответа секретаря

Так же мы можем предложить позвонившему соединится необходимым специалистом из отдела, который он выбрал выше. Так, например, если он выбрал первый отдел, то мы можем воспроизвести ему следующую информацию:
  • Вы находитесь в пункте меню отдел 1
  • Чтобы соединится со специалистом по %X% нажмите 5
  • Чтобы соединится со специалистом по %Y% нажмите 6
  • Для возврата в предыдущее меню нажмите 0


Приступим к реализации! Допустим что у нас имеется номер 8-495-1234567 на котором мы будем «растить» наше дерево. Вся входящая и исходящая маршрутизация в Asterisk, как правило, расположена в файле extensions.conf, который находится в корневой папке астериска — /etc/asterisk. Я считаю, что Ivr-деревья удобнее создавать отдельными файлами поэтому создадим в корневой папке Asterisk файл company.tree и впишем в него пока одну лишь строчку:

[company_tree]

На этом оставим редактирование файла company.tree и перейдем к файлу extensions.conf — в начале данного файла добавим следующее:

#include "company.tree"

Чтобы подгрузить недавно созданный файл в общий план маршрутизации Asterisk. Теперь перейдем основной контекст для входящих звонков (у всех он может называться по разному — general, from_pstn, from_e1 или как кому больше нравиться) и в него добавим следующую строчку:

exten => 84951234567,1,Goto(company_tree,s,1)

При входящем звонке на номер 84951234567 Asterisk переведет нас в контекст company_tree на первую строку «s», который находится в файле company.tree, который мы благополучно подгрузили в общий план маршрутизации выше. Звучит немного запутанно, однако подобная вложенность поможет нам избежать случаев некорректной маршрутизации из-за пересечения нескольких контекстов в файле extensions.conf.

На данном этапе перейдем в файл company.tree и собственно начнем «строить» Ivr-дерево — добавим в него следующие строки:

exten => s,1,Answer()
#Отвечаем на входящий звонок
exten => s,2,Background(/home/ulaw/IVR-zapis)
#Проигрываем запись текста нашего голосового дерева*
exten => s,3,WaitExten(7)
#Немного времени на осмысление дерева и выбор интересующего направления, в данном случае 7 секунд

exten => 1,1,Goto(otdel1,s,1)
#Делаем переход в контекст первого отдела по нажатию клавиши 1
exten => 2,1,Goto(otdel2,s,1)
#Аналогично

exten => 9,1,Set(FAXFILE=/tmp/fax/${STRFTIME(${EPOCH},,%Y%m%d_%H_%M_%S)}-from-${CALLERID(num)}) #Тут мы принимаем факсы, первым делом устанавливаем факсимильному файлу - время во сколько пришел факс и с какого номера он пришел
exten => 9,2,ReceiveFax(${FAXFILE}.tif)
# Функция приема факса с именем который мы указали выше
exten => 9,3,System(sendEmail -f fax@company.com -t you@company.com -u "Входящий факс." -m "Вам пришел факс с номера ${CALLERID(num)} в ${STRFTIME(${EPOCH},,%H:%M:%S)}. Факс во вложении." -a ${FAXFILE}.tif -o message-charset=UTF-8)
#Пересылка пришедшего файла на электропочту
exten => _XXX,1,Dial(SIP/${EXTEN}@YourAsterisk)
#Если тот кто звонит знает внутренний номер сотрудника, то для эта строчка для него :)

exten => t,1,Dial(SIP/внутренний номер секретаря@YourAsterisk)
#Этот звонок пойдет по таймауту на секретаря, если за 7 секунд в голосовом дереве не было выбрано направление

exten => i,1,Dial(SIP/внутренний номер секретаря@YourAsterisk)
#Этот звонок пойдет на секретаря, если абонент выбрал несуществующее направление


[otdel1]
# контекст пункта меню - первый отдел
exten => s,1, Background(/home/wav/otdel1)
# Проигрываем - "Вы находитесь в пункте меню отдел 1"
exten => s,2,Waitexten(7)
# Ждем решения позвонившего
exten => 1,1,Dial(SIP/внутренний номер@YourAsterisk)
#Соединяем со специалистом %X%
exten => 2,1,Dial(SIP/внутренний номер@YourAsterisk)
#Соединяем со специалистом %Y%
exten => t,1,Dial(SIP/внутренний номер@YourAsterisk)
#Соединяем с кем-нибудь по таймауту
exten => 0,1,Goto(company_tree,s,1)
#Делаем переход на вышестоящий пункт меню


На этом дерево готово. Осталось только обновить маршрутизацию астериска — для этого неоходимо в терминале выполнить:

asterisk -r
dialplan reload


*В качестве аудио файлов в Asterisk используются файлы с расширением *.ulaw, необходимо помнить, что при составлении голосовых деревьев, в тех местах где указываются пути к аудио расширение файлов не указывается — например «Background(/home/IVR/zapis1)». Для работы с данным форматом я использовал ПО — CoolEdit и Adobe Audition, если кто знает еще звуковые редакторы с поддержкой и конвертацией ulaw, напишите буду признателен.
+26
25644
116
Fullgrim 3,5

Комментарии (54)

0
zloe, #
Поток Е1 цифровой.
0
zloe, #
Есть вероятность что я недопонял смысл фразы.
Лучше замените "/" на «или»
0
Fullgrim, #
спасибо пофиксил
+1
TemaMak, #
зря не включили в контексты экстеншен i (illegal), что бы подхватывать те клавишы, которые не ожидались.
0
Fullgrim, #
В работе постоянно их включаю :) тут решил не упоминать деревце то простое
0
TemaMak, #
Если кто-то будет учиться по Вашей статье — шишок набьет во время эксплуатации. В его текстах все отработает, а живые люди начнут нажимать незаложенные в меню клавиши.
0
bad_guy, #
Папка с звуковыми файлами лежит в «wav», а расширения файлов «ulaw», а сама АТС работает через кодек Alaw. Видя такие моменты, в начале, всегда сомневался — опечатка или нет. Кстати, можно использовать и wav-формат(я его и использовал), но тогда надо использовать PCM-кодек, 16 бит с частотой 8000Гц.
0
Fullgrim, #
Да по сути не важно как называется папка, но согласен, что может запутать, за сим исправлю. Ну а по поводу расширения ulaw, то его ставит сам аудиоредактор у которого механизм конвертации выбран alaw/Mulaw. Wav файлы тоже использовал, но они сильнее грузят систему, да и места занимают больше.
НЛО прилетело и опубликовало эту надпись здесь
0
bad_guy, #
Хм… А если я закину три файла с одинаковыми именами, но разными расширениями, что из этого подцепит Астериск?
0
miga, #
По возможности тот, который закодирован тем же кодеком, что и в медиа-потоке идет.
НЛО прилетело и опубликовало эту надпись здесь
0
Suncheez, #
Хочу заметить несколько моментов:
1. Астериску совершенно парараллельно на формат файлов. Лишь бы он был обучен их проигрывать. Лично у меня — gsm
2. Существенно меньшее кол-во глюков и заметный прирост в качестве звука получается, если создавать файлы в Wav-44КГц, а затем на машине с астериском конвертировать их в нужный формат с необходимым битрейтом программой sox.
3. Перекидывать сразу на секретаря при i я не стал. Все инвалиды идут по кругу на файл %lang%-route, пусть слушают внимательно, прежде чем нажимать.
4. Таймауты тоже сначала слушают %lang%-route ещё раз, а только потом перекидываются на секретаря.
0
Watcher, #
Инвалид (i) — это человек. И звонит он скорее всего по делу. Не надо портить ему нервы лишним временем в меню. Не смог соединиться — на секретаря, пускай дальше сек направит. Есть хорошие SIP-аппараты для секретарей с кнопочными консолями, скажем, Cisco SPA932 с SPA962. На кнопки навешиваются наиболее часто вызываемые и все, секретарь за пару нажатий кнопки переправит куда надо.
+2
Fullgrim, #
На днях попал к нам на тест потенциальный телефон секретаря, даже волпаперы можно ставить :)
0
miga, #
А у меня есть его младший брат. Несмотря что на трубке написано HD, а сам телефон умеет HD-кодеки, звук у него так себе :)
image
+1
Fullgrim, #
В идеале при неправильно нажатой цифре лучше проиграть сообщение, что выбрано несуществующее направление и заново проиграть меню. Но соединение с секретарем по таймауту спасет пользователей, телефонные аппараты которых находятся в импульсном режиме.
0
Watcher, #
Первое желаение у любого нормального человека, когда он услышит автоматическое меню, бросить трубку. Поэтому каждая секунда проведенная в меню — критична.

Самое главное — сделать так, чтобы цифры отделов в меню не совпадали с первой цифрой номеров внутренних абонентов. Номер 104. Если в меню будет 1 — скажем отдел продаж, то после нажатия 1 астериск 5 секунд будет ждать, а вдруг юзер наберет еще «04»…
0
Suncheez, #
Мы сделали меню максимально кратким. С момента поднятия трубки роботом до окончания меню первого уровня проходит 21 секунда. Со второй секунды робот слушает ввод пользователя.

Ваше «самое главное» совершенно невыполнимо, если меню состоит из 10 пунктов.

Чтобы робот не слушал 5 секунд, надо ставить таймаут в три секунды.
Чтобы робот переключался сразу, как только услышал цифру, надо ставить в её начале символ подчёркивания.
0
Yur4eg, #
Подчеркивание в начале — это признак шаблона (pattern). Например XXX — это три икса для астериска, а _XXX — три любые цифры. Когда пользователь нажимает на клавишу, во время команды Background, астериск ищет в текущем контесте, совпадающий номер и переходит на него, если под набранную комбинацию попадает более одного шаблона, то астериск ждет донабора еще одной цифры. Этот таймаут регулируется функцией TIMEOUT(digit)
0
FSA, #
extensions.ael гораздо легче читается. А ещё есть extensions.lua, но говорят если не знаешь этого lua лучше не соваться. Если кто иного мнения, отписывайтесь.
0
miga, #
extensions.conf легче в плане программной генерации :)
–2
mulai, #
DTMF, DTMF,…
XXI век на дворе, господа. Для Астериска давно есть UniMRCP. Пора учить системы понимать человеческий голос:))
0
litnimax, #
Опытом не поделитесь?
0
mulai, #
ОК, в ближайшее время постараюсь.
0
wGG, #
Речь идет о связке с VoiceNavigator?
0
mulai, #
Да. Хотя в теории можно подключить любую платформу, работающую по MRCP.
0
wGG, #
Пощупал недавно VN, дорого, но качественно.
+1
litnimax, #
Все, кто строит IVR на Asterisk, и использует русские голосовые файлы отсюда — downloads.asterisk.org/pub/telephony/sounds/, т.е. те файлы, которые вот вот войдут в состав Asterisk. Так вот, Вы можете заказать дозапись нужных вас фраз, ведь пакет подготовлен студией ivrvoice.ru ;-)
Можно записать единственную фразу с именем компании, остальные пару сотен фраз пойдут в виде бесплатной нагрузки.
0
Yur4eg, #
Уберите WaitExten(7), если клиент затупил, то надо его сразу на секретаря переводить. Не будет он семь секунд в тишине сидеть, трубочку повесит и все. Еще у вас в факсах не хватает обработки ошибок во время приема.
Кстати, вместо:
exten => s,1,Answer()
exten => s,2,Background(/home/ulaw/IVR-zapis)
exten => s,3,WaitExten(7)

Сейчас пишут:
exten => s,1,Answer()
exten => s,n,Background(/home/ulaw/IVR-zapis)
exten => s,n,WaitExten(7)

Астериск сам умеет рассчитывать приорететы по положению команд в файле.
0
Fullgrim, #
По поводу WaitExten, то его убирать я не вижу смысла возможно сделать в районе 3 секунд, но не совсем убирать.
Ну а по поводу расставления приоритетов — оно отработается в обоих случаях просто когда у меня 3 строки одного идентификатора, то несложно можно расставить приоритеты самому, когда 50, тогда можно уже и «n» использовать
0
qmax, #
«Чтобы отправить факс нажмите 9»
exten => 9,2,ReceiveFax(${FAXFILE}.tif)
и как, получается принмать факсы с аналоговых линий?
0
miga, #
Вполне, и отправлять тоже.
И по Т.38 уже чаще работает, чем нет :)
0
Fullgrim, #
C аналоговых линий вообще счастье одно факсы принимать :)
А VoIP — это вечная проблема(то джитер большой, то пакет потерялся).
0
miga, #
Скорее, не проблема, а вопрос кармы и везения. У особо счастливых бывало что факсы успешно смогли по VoIP (sic!) в 711 кодеке насвистеть что-то, без всяких T.38 :)
0
udachnik, #
А что удивительного? Вот если б в 709, тогда действительно было бы удивительно. У меня везде 711а с pass through проблем не наблюдаю (канал везде хороший)
+1
miga, #
Удивителен сколько не сам факт прохождения, сколько то, что это через VoIP-провайдера прошло. В pass-through через какой-нибудь SPA-2102 и Digium/Openvox в медь оно как раз весьма хорошо ходит.
0
udachnik, #
Понял, у меня просто я сам провайдер, а дальше е1…
0
akshakirov, #
Через 711 это вообще рулетка, на часть направлений может проходить на часть нет. Иногда операторы на стыках пережимают 711 и всё, капец факсу.

t.38 тоже не всегда рулит, у одного местного оператора видимо реализация не совместимая с t38modem.
0
qmax, #
exten => ххх,1,
exten => ххх,2,
exten => ххх,3,
exten => ххх,4,

Считаю, должно караться через карапупу, с запретом пользоваться арифметикой и оператором goto на 7 лет.
0
mesline, #
Хотелось бы уточнить:
1. А какая версия asterisk у Вас используется?
2. Может опубликуете полный диалплан внутренних вызовов в офисе и в город с перехватом неправильных номеров примерно так как сделано в trixbox если не ошибаюсь. (ну не Ваш конечно, а примерный диалплан)
3.Интересна реализация видеозвонков через программные voip клиенты.
4.Реализация конференций как голосовых так и видео.
5.Кто-то на хабре (или в журнале хакер это читал) писал вроде о том как можно звонить с сотового на астериск, астериск сам сбрасывает звонок в случае определения номера сотового, потом перезванивает на сотовый и дает возможность позвонить в офис или еще куда, тем самым пользователю сотового бесплатно получится — интересный момент для кол центров.
6.Есть еще одна проблема кол-центров это обрыв связи, человек перезванивает и может попасть другому оператору, а желательно к тому же.
У меня до всего этого тупо времени нет, но весь этот функционал хотелось бы использовать в фирме.
Была бы подробная статья как сделать это быстро и качественно — было бы очень кстати.
P.S. также интересно голосовая почта и обмен текстовыми сообщениями voip клиентов.
Эх, еще бы аналог trixbox сваять для русских пользователей и нужд без особых знаний во всей этой кухне :)

0
mesline, #
Странно, вроде писал комментарий, а получился ответ.
0
miga, #
3. Это не зависит от астериска. Включаете видео-кодек в SIP-пире и дальше уже если софтфон умеет — будет видео, нет — извините :)
4. Голосовая — просто кидаете канал в диалплане MeetMe(<conf-id>). Видеоконференция требует стороннего решения для микширования видеопотоков. Мы делали через red5 и flash, получилось нечто вроде многопользовательской чатрулетки.
5. Если интересно, могу показать, как это реализуется в диалплане.
Голосовая почта это стандартная фича, в voicemail.conf накидали ящиков и вперед :)
Текстовые сообщения хоят и поддерживаются SIP, астериск, можно сказать, не умеет. Хотя некоторые телефоны могут принимать и осмысленно показывать SIP-сообщения, отправляемые через sipsak, например.
0
Avenit, #
Что-то не увидел никаких деревьев тут, обычный линейный IVR, даже в книжке «Asterisk — будущее телефонии» он более функционально и подробно описан.

В команде регистрации на провайдере лучше указывать добавочный номер и принимать звонки на него, а не на s — экстеншн, это позволит принимать и различать звонки когда у вас больше 1 внешнего номера.

Это что вообще за формат такой?
exten => _XXX,1,Dial(SIP/${EXTEN}@YourAsterisk)
Все проще:
exten => _XXX,1,Dial(SIP/${EXTEN})

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

Это вообще убило…
exten => s,1,Answer()
exten => s,2,Background(/home/ulaw/IVR-zapis)
exten => s,3,WaitExten(7)


Уже давно можно делать так:
exten => s,1,Answer
same => n,Background(/home/ulaw/IVR-zapis)
same => n,WaitExten(7)


Файлы лучше ложить в /var/lib/asterisk/sounds/ru/ivr, где ru — язык канала, а ivr — подпапка с вашими записями. Тогда вызывать можно по относительному gути:
same => n,Background(ivr/zapis)

Насчет формата файлов: ulaw — используется в северной америке, в европе используют alaw. Для конвертации можно воспользоваться sox и самим Asterisk:
sox <file_in>.wav -r 8000 -c 1 <file_out>.wav
asterisk -rx 'file convert <file_in> <file_out>'
0
Fullgrim, #
Это вообще убило…
exten => s,1,Answer()
exten => s,2,Background(/home/ulaw/IVR-zapis)
exten => s,3,WaitExten(7)

Уже давно можно делать так:
exten => s,1,Answer
same => n,Background(/home/ulaw/IVR-zapis)
same => n,WaitExten(7)


Что изменится то? Функционал один и тот же, скажем, это дань уважения ныне почившим версиям астериска. И это вопрос поднимался в комментариях выше.

По поводу
Это что вообще за формат такой?
exten => _XXX,1,Dial(SIP/${EXTEN}@YourAsterisk)
Все проще:
exten => _XXX,1,Dial(SIP/${EXTEN})

0
Fullgrim, #
Отчасти с вами согласен, но этот формат будет полезен, когда внутренние абоненты подключены через голосовые шлюзы, правда формат тогда будет не совсем такой:
0
Fullgrim, #
exten => _XXX,1,Dial(SIP/${EXTEN}@YourDevice)

P.S. прошу прощения за такой длинный комментарий, запал enter :)
0
udachnik, #
>>Это что вообще за формат такой?
>>exten => _XXX,1,Dial(SIP/${EXTEN}@YourAsterisk)
Это звонок на другой сервер с asterisk, например.
+1
Avenit, #
Это устаревший синтаксис, использовался еще до 1.4.
Для звонков на известные серверы лучше создавать записи в sip.conf и звонить так:
exten => _XXX,1,Dial(SIP/trunk_provider/${EXTEN})

А Ваша запись не позволяет задавать например параметры кодеков и многое другое.
0
Fullgrim, #
У меня данный синтаксис успешно работает и на 1.4.39 и на 1.6, это дело вкуса.
Параметры кодеков мне задавать нет необходимости, везде g729(да, экономим канал)
Но согласитесь, что мало разницы между:

exten => _XXX,1,Dial(SIP/trunk_provider/${EXTEN})
и
exten => _XXX,1,Dial(SIP/${EXTEN}@trunk_provider)
0
Avenit, #
Так вы это используете для локальных пиров!
0
udachnik, #
Именно так, YourAsterisk — это обычный пир, с настройками кодека, и т.д., и именно этот синтаксис использую в 1.8
0
udachnik, #
Сейчас поднял документацию, вы правы насчет устарелости — в доке к 1.8 указан только ваш способ, про @ ни слова. Но астериск еще не говорит что deprecated. Буду знать.
0
DjOnline, #
Предположим IVR приветствие длится 30 секунд.
Как заставить asterisk/freepbx сразу начать перебор операторов при входящем звонке, не ожидая этих 30 секунд, и досрочное прекращение IVR если оператор взял трубку раньше 30 секунд?

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