Устанавливаем голосовую почту в в Asterisk
Продолжаем наращивать функционал voip атс. В данном разделе опишу настройку голосовой почты в asterisk. Для начала пару слов о том, что это такое. Если адресат звонка долго не отвечает, мы можем предложить звонящему оставить для него голосовое сообщение. Когда получатель вернется на место, он сможет прослушать оставленные ему сообщения. При этом, после записи голосового сообщения, будет отправлено письмо с записью этого сообщения на почтовый адрес получателя.
На практике я не видел, чтобы голосовую почту активно использовали. Но для полноты картины расскажу про нее, возможно вам она пригодится. Открываем файл voicemail.conf на редактирование. Я не трогал настройки по-умолчанию, просто добавляем в секцию [default] ящики голосовой почты для нужных нам сотрудников в следующем виде:
130 => 1234,Number 130,user130@mail.ru 100 => 1234,Number 100,user100@mail.ru
130 | внутренний номер абонента |
1234 | пароль доступа к ящику голосовой почты |
user130@mail.ru | почтовый адрес, куда будет отправлено записанное голосовое сообщение |
Для корректной отправки почтовых сообщений сразу на внешние почтовые ящики необходимо правильно настроить локальный почтовый сервер, либо использовать внешний. Я рекомендую использовать отдельный сервер, наверняка он есть в организации, либо на локальном использовать какой-то публичный с авторизацией по smtp. Пример такой настройки — отправка почты с авторизацией по smtp в linux.
В консоли перезапускаем модуль голосовой почты и проверяем пользователей:
CLI> voicemail reload Reloading voicemail configuration... asterisk*CLI> voicemail show users Context Mbox User Zone NewMsg default 130 Number 121 0 default 100 Number 100 0 other 1234 Company2 User 0 3 voicemail users configured.
Наши два добавленных пользователя и один тестовый остался из дефолтной конфигурации. Его можно удалить. Их вообще два должно быть. Одного я уже удалил, второго забыл.
Это пол дела. Теперь нам нужно добавить голосовую почту в dialplan. Причем в 2 разных места. Я буду использовать номер 500 для звонка в панель управления голосовой почтой. Позвонив на этот номер, пользователь введет свой пароль и сможет управлять голосовыми сообщениями (слушать, удалять, менять настройки). Добавим в контекст для исходящих звонков звонок на этот номер. Добавлять следует сразу за номерами для записи, которые мы ранее создали и перед правилом набора трехзначных номеров.
[call-out] ;Номер для записи звуков, окончание записи # exten => _35X, 1, NoOp() exten => _35X, n, Wait(2) exten => _35X, n, Playback(beep) exten => _35X, n, Record(/tmp/music${EXTEN:2}:wav) exten => _35X, n, Wait(1) exten => _35X, n, Playback(/tmp/music${EXTEN:2}) exten => _35X, n, Wait(2) exten => _35X, n, Hangup() ;Управление голосовой почтой exten => 500,1,VoiceMailMain() ;Звонок на внутренний номер exten => _XXX,1,Dial(SIP/${EXTEN},15) ;Звонок на внешний номер exten => _XXX.,1,Dial(SIP/${EXTEN}@397945)
Добавленные данные выделил цветом. Обращаю внимание на цифру 15. Ранее этой настройки не было, сейчас я добавил. Она будет означать, что звонок будет длиться 15 секунд. Если за это время никто не ответит, он будет сброшен. До использования голосовой почты, можно было не устанавливать этот параметр, оставить его значение по-умолчанию, оно очень большое, не помню точно сколько по времени. Но сейчас нам нужно при неснятии трубки дольше 15-ти секунд, включать запись голосового сообщения.
Конкретно в контексте внутренних звонков этот параметр не принципиален, так как я не добавляю голосовую почту для звонков внутри организации, хотя можно это сделать. Не вижу в этом смысла. Я добавил сюда этот параметр, чтобы время ожидания ответа было одинаково во всех звонках. В следующих изменениях это уже будет играть принципиальное значение.
Пока мы просто добавили номер, куда можно позвонить для управления голосовой почтой. Теперь добавим непосредственно возможность записи голосовых сообщений по событиям. Остановимся на этом пункте поподробнее. Для начала уясним, в каких состояниях может пребывать номер:
- Номера вообще не существует на сервере. В нашем случае, к примеру, это любой номер не из диапазона 100-130.
- Номер существует, но он не зарегистрирован на АТС, то есть аппарат с этим номером не подключен.
- Номер существует, зарегистрирован, но при звонке на него никто не отвечает.
- Номер существует, зарегистрирован, но в данный момент занят.
Каждое из этих четырех состояний обрабатывается отдельно. Я считаю, что голосовую почту уместно будет включать по событию номер 3. Но это не обязательно, можно и на занято повесить возможность оставить сообщение. Тут на ваше усмотрение. Я покажу пример, как это делается, а вы сможете настроить так, как вам нужно.
Обработка этих событий не такая простая, как кажется на первый взгляд. Я сразу же столкнулся с трудностью следующего характера. С параметром call-limit=1 при звонке на номер, который занят в данный момент, астериск возвращает статус CHANUNAVAIL, что может означать, к примеру, что канал недоступен. На статус занято BUSY это совсем не похоже. Для разрешения этой ситуации я воспользуюсь функцией ChanIsAvail, которая проверяет не статус пира, а статус канала и возвращает значение 2 или 3, когда он занят.
Для различения несуществующих и не подключенных пиров я буду использовать функцию SIPPEER. Если она ничего не возвращает, значит номера не существует, если значение UNKNOWN, значит номер не подключен. С отсутствием ответа какое-то время проще всего. Если пир возвращает статус NOANSWER, включаем голосовую почту. Собираем все статусы в одно место и добавляем голосовую почту. Для этого в контекст [ivr-main] добавляем новые параметры и приводим его к следующему виду:
[ivr-main] exten => s,1,Answer() exten => s,2,Background(/etc/asterisk/ivr/ivr-main) exten => s,3,WaitExten(5) exten => _XXX,1,Dial(SIP/${EXTEN},15) ;Задаем переменную для передачи в голосовую почту exten => _XXX,n,Set(dstNUM=${EXTEN}) ;Проверяем статус пира, существует или нет exten => _XXX,n,GotoIf($["${SIPPEER(${EXTEN},status)}" = ""]?num-not-exist,1) ;Проверяем статус пира, подключен или нет exten => _XXX,n,GotoIf($["${SIPPEER(${EXTEN},status):0:2}" = "UN"]?num-not-connected,1) ;Проверяем канал на занятость exten => _XXX,n,ChanIsAvail(SIP/${EXTEN},s) ;Выводим в лог значение функции ChanIsAvail, нужно только для отладки, можно удалить строку exten => _XXX,n,NoOp(=========== ChanIsAvail STATUS: ${AVAILSTATUS} ===========) ;Если функция возвращает 2 или 3, значит абонент занят exten => _XXX,n,GoToIf($[${AVAILSTATUS} = 2]?num-BUSY,1) exten => _XXX,n,GoToIf($[${AVAILSTATUS} = 3]?num-BUSY,1) ;Обрабатываем остальные статусы exten => _XXX,n,Goto(num-${DIALSTATUS},1) ;Если номера не существует говорим "Ошибочный номер, попробуйте еще раз" exten => num-not-exist,1,Wait(2) exten => num-not-exist,n,Playback(invalid) ;Если номер не подключен, говорим "Набранный вами номер отключен, проверьте номер и повторите попытку exten => num-not-connected,1,Wait(2) exten => num-not-connected,n,Playback(ss-noservice) ;Если номер занят, говорим "Занято" exten => num-BUSY,1,Wait(2) exten => num-BUSY,n,Playback(vm-isonphone) ;Если номер не отвечает, включаем голосовую почту exten => num-NOANSWER,1,Wait(2) exten => num-NOANSWER,n,Voicemail(${dstNUM},u) ;Если еще по какой-то причине будет статус CHANUNAVAIL, говорим, что номер не доступен в данный момент exten => num-CHANUNAVAIL,1,Wait(2) exten => num-CHANUNAVAIL,n,Playback(vm-isunavail) ;Если в голосовом меню не выбрали внутренний номер, адресуем звонок секретарю exten => t,1,Dial(SIP/100,15)
Если вам нужна обработка статусов номеров для внутренних звонков, то скопируйте обработку состояний в контекст [call-out], за исключением последней строки, которая отвечает за звонок секретарю. Полностью контекст внутренних звонков будет выглядеть вот так:
[call-out] ;Номер для записи звуков, окончание записи # exten => _35X, 1, NoOp() exten => _35X, n, Wait(2) exten => _35X, n, Playback(beep) exten => _35X, n, Record(/tmp/music${EXTEN:2}:wav) exten => _35X, n, Wait(1) exten => _35X, n, Playback(/tmp/music${EXTEN:2}) exten => _35X, n, Wait(2) exten => _35X, n, Hangup() ;Управление голосовой почтой exten => 500,1,VoiceMailMain() ;Звонок на внутренний номер exten => _XXX,1,Dial(SIP/${EXTEN},15) exten => _XXX,n,Set(dstNUM=${EXTEN}) exten => _XXX,n,GotoIf($["${SIPPEER(${EXTEN},status)}" = ""]?num-not-exist,1) exten => _XXX,n,GotoIf($["${SIPPEER(${EXTEN},status):0:2}" = "UN"]?num-not-connected,1) exten => _XXX,n,ChanIsAvail(SIP/${EXTEN},s) exten => _XXX,n,NoOp(=========== ChanIsAvail STATUS: ${AVAILSTATUS} ===========) exten => _XXX,n,GoToIf($[${AVAILSTATUS} = 2]?num-BUSY,1) exten => _XXX,n,GoToIf($[${AVAILSTATUS} = 3]?num-BUSY,1) exten => _XXX,n,Goto(num-${DIALSTATUS},1) exten => num-not-exist,1,Wait(2) exten => num-not-exist,n,Playback(invalid) exten => num-not-connected,1,Wait(2) exten => num-not-connected,n,Playback(ss-noservice) exten => num-BUSY,1,Wait(2) exten => num-BUSY,n,Playback(vm-isonphone) exten => num-NOANSWER,1,Wait(2) exten => num-NOANSWER,n,Voicemail(${dstNUM},u) exten => num-CHANUNAVAIL,1,Wait(2) exten => num-CHANUNAVAIL,n,Playback(vm-isunavail) ;Звонок на внешний номер exten => _XXX.,1,Dial(SIP/${EXTEN}@397945) include => handup-sip
Я во всех звонках установил время ожидания ответа 15 секунд. Если вы считаете, что нужно больше, измените этот параметр.
Перечитывайте диалплан и тестируйте конфигурацию. При звонке абоненту и его неответе, звонящий услышит в трубке сообщение о том, что номер не отвечает и предложение оставить голосовую почту. Если звонивший оставит сообщение, то получатель получит это сообщение по email и сможет его прослушать там, либо позвонить на номер 500, ввести свой номер и пароль, заданные в voicemail.conf и послушать сообщение по телефону.
Я сталкивался с ошибкой, когда у пользователя в настройках отключена голосовая почта, но он все равно получал сообщения. У него мигало оповещение на телефонном аппарате. Нужно было оперативно удалить все оставленные сообщения без прослушивания. Как удалить голосовую почту в астериск я написал в отдельной заметке.
На этом настройка голосовой почты окончена. Можно пользоваться, не забывая добавлять новых пользователей в voicemail.conf и перечитывая конфигурацию голосовой почты.
Очереди (queues) входящих звонков
С помощью очередей в астериске можно управлять потоком входящих звонков, перераспределяя их по определенным правилам. В нашей конфигурации asterisk мы настроим очередь (queue) для направления звонка двум секретарям одновременно. Кто первый ответит, тот и будет разговаривать со звонящим. Если один секретарь уже разговаривает, новый звонок поступит к другому. Если оба секретаря будут заняты, звонящий будет слушать мелодию и ожидать, пока кто-нибудь не освободится. Как только один из секретарей освободится, звонящего из очереди направит на освободившийся номер.
Номер первого секретаря — 100, второго — 130. Я настраиваю простейшую конфигурацию очереди в астериск для понимания принципа работы. Более сложный вариант настройки это сделать 3 очереди для каждого отдела и в голосовом меню-приветствии сделать возможность позвонить в конкретный отдел. Эту конфигурацию я рассмотрю в отдельной статье, хотя в нем и нет ничего сложного. Делается по аналогии с приведенным примером.
Открываем файл queues.conf и добавляем в самый конец:
[secretary] strategy = ringall member => SIP/100 member => SIP/130
Все остальные настройки оставляю по-умолчанию. Параметр strategy может принимать следующие значения:
ringall | вызываются все доступные участники до тех пор, пока кто-то из них не ответит на вызов (по умолчанию). |
leastrecent | Вызывается первый свободный участник, который меньше всего вызывался из этой очереди. |
fewestcalls | Вызывается первый свободный участник, который обработал наименьшее количество вызовов из данной очереди. |
random | случайным образом вызывается не занятый участник, обрабатывающий очередь. |
rrmemory | циклическое распределение с памятью, запоминается последний участник, ответивший на вызов. |
Вы можете выбрать наиболее подходящую вам стратегию распределения звонков в очереди. Дальше нужно добавить в extensions.conf в созданный нами ранее контекст с голосовым меню отправку звонка в очередь с секретарями. Для этого меняем строку в [ivr-main]:
exten => t,1,Dial(SIP/100,15)
на новую:
exten => t,1,Queue(secretary,t)
Если раньше при звонке на внешний номер, звонящий не набирал внутренний номер абонента, то через 5 секунд он перенаправлялся к секретарю с номером 100. Теперь он будет отправляться в очередь secretary, в которую мы завели 2 номера — 100 и 130. Можно добавить и больше номеров, если есть необходимость.
Перечитываем полностью конфигурацию asterisk:
CLI> reload
Состояние созданной очереди:
CLI> queue show secretary secretary has 0 calls (max unlimited) in 'ringall' strategy (0s holdtime, 0s talktime), W:0, C:0, A:0, SL:0.0% within 0s Members: SIP/100 (ringinuse enabled) (Not in use) has taken no calls yet SIP/130 (ringinuse enabled) (Not in use) has taken no calls yet No Callers
Теперь можете позвонить на внешний номер и дождаться перенаправления на секретаря. Зазвонят оба аппарата. Если оба заняты, звонок будет висеть в очереди и ждать освобождения. После звонка статистика очереди изменится:
CLI> queue show secretary secretary has 0 calls (max unlimited) in 'ringall' strategy (0s holdtime, 2s talktime), W:0, C:1, A:1, SL:0.0% within 0s Members: SIP/100 (ringinuse enabled) (Not in use) has taken 1 calls (last was 63 secs ago) SIP/130 (ringinuse enabled) (Not in use) has taken no calls yet No Callers
Вот так легко организовать простую queue (очередь) в asterisk. Более сложные примеры я буду рассматривать в отдельных статьях.
Учет и просмотр статистики звонков (cdr viewer)
Важной и нужной возможностью современной АТС на базе asterisk является сбор и просмотр статистики звонков. По умолчанию, астериск ведет статистику в файле /var/log/asterisk/cdr-csv/Master.csv. Разобрать этот файл и передать куда-то в обработку не очень сложно, если у вас есть что-то или кто-то, кто способен написать на каком-нибудь языке программирования обработку.
Мы будем использовать готовые бесплатные инструменты для просмотра статистики звонков. Все необходимое для этого мы установили в самом начале. Перенесем сбор статистики в mysql базу. Для этого запускаем mariadb сервер, добавляем в автозагрузку и устанавливаем пароль администратора:
# systemctl start mariadb # systemctl enable mariadb.service # /usr/bin/mysql_secure_installation
Подключаемся к mysql и создаем пользователя и базу данных:
# mysql -uroot -p MariaDB [(none)]> create database asterisk; MariaDB [(none)]> use asterisk; MariaDB [asterisk]> CREATE TABLE `cdr` ( `id` int(9) unsigned NOT NULL auto_increment, `calldate` datetime NOT NULL default '0000-00-00 00:00:00', `clid` varchar(80) NOT NULL default '', `src` varchar(80) NOT NULL default '', `dst` varchar(80) NOT NULL default '', `dcontext` varchar(80) NOT NULL default '', `channel` varchar(80) NOT NULL default '', `dstchannel` varchar(80) NOT NULL default '', `lastapp` varchar(80) NOT NULL default '', `lastdata` varchar(80) NOT NULL default '', `duration` int(11) NOT NULL default '0', `billsec` int(11) NOT NULL default '0', `disposition` varchar(45) NOT NULL default '', `amaflags` int(11) NOT NULL default '0', `accountcode` varchar(20) NOT NULL default '', `uniqueid` varchar(32) NOT NULL default '', `userfield` varchar(255) NOT NULL default '', PRIMARY KEY (`id`), KEY `calldate` (`calldate`), KEY `accountcode` (`accountcode`), KEY `uniqueid` (`uniqueid`), KEY `dst` (`dst`), KEY `src` (`src`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; MariaDB [asterisk]> grant all on asterisk.* to 'asterisk_user'@'localhost' identified by '12345678';
asterisk | имя базы данных |
asterisk_user | пользователь базы данных |
12345678 | пароль пользователя бд |
Теперь нам надо установить odbc коннектор для mysql — mysql-connector-odbc. С этим в Centos 8 есть некоторые трудности, так как нужных пакетов нет в базовых репозиториях. Я в итоге его взял из официальной репы mysql. Для этого скачиваем и подключаем их репозиторий.
# wget https://dev.mysql.com/get/mysql80-community-release-el8-1.noarch.rpm # dnf localinstall mysql80-community-release-el8-1.noarch.rpm # dnf makecache
Тут я получил ошибку:
Failed to download metadata for repo 'mysql-tools-community' Error: Failed to download metadata for repo 'mysql-tools-community'
Не стал разбираться, почему этот репозиторий недоступен, так как он мне не нужен. Просто зашел в /etc/yum.repos.d/mysql-community.repo и закомментировал строки с этим репозиторием.
#[mysql-tools-community] #name=MySQL Tools Community #baseurl=http://repo.mysql.com/yum/mysql-tools-community/el/8/$basearch/ #enabled=1 #gpgcheck=1 #gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql
После этого благополучно обновил кэш пакетов и установил mysql-connector-odbc на centos 8.
# dnf install mysql-connector-odbc
Теперь редактируем файлы конфигурации. Добавляем в самый конец /etc/asterisk/res_odbc.conf:
[asterisk] enabled => yes dsn => MySQL-asterisk username => asterisk_user password => 12345678
В конец файла /etc/asterisk/cdr_adaptive_odbc.conf:
[cdr_adaptive_connection] connection=asterisk table=cdr alias start => calldate
Создаем файл /etc/odbc.ini следующего содержания:
[MySQL-asterisk] Description = MySQL Asterisk database Driver = MySQL Server = localhost User = asterisk_user Password = 12345678 Socket = /var/lib/mysql/mysql.sock Database = asterisk
Редактируем файл /etc/odbcinst.ini. Я его не трогал, оставил по-умолчанию, только в самый конец секции [MySQL] добавил две недостающие строки. Я не разбирался нужны они или нет, просто подсмотрел в другой инструкции. Вот как этот файл выглядит у меня:
[MySQL] Description = ODBC for MySQL Driver = /usr/lib/libmyodbc8.so Setup = /usr/lib/libodbcmyS.so Driver64 = /usr/lib64/libmyodbc8a.so Setup64 = /usr/lib64/libodbcmyS.so FileUsage = 1 CPTimeout = CPReuse =
В последнем конфиге проверьте все пути. Имена файлов могут немного отличаться в зависимости от установленной версии коннектора. Актуализируйте пути и имена файлов. После этого проверьте работу odbc connector. Для этого запустите в консоли команду:
# odbcinst -q -d
Вы должны увидеть список всех настроенных коннекторов, в том числе [Mysql]. Настроим использование нашего часового пояса в записях cdr. По-умолчанию там стоит часовой пояс GTM. Для этого в файле /etc/asterisk/cdr.conf указываем параметр:
usegmtime=no
После этого перезапускаем астериск:
# systemctl restart asterisk
Заходим в консоль и проверяем подключение по odbc.
> odbc show all
Совершаем звонок и проверяем таблицу mysql. У меня добавилась информация о звонке с номера 100 на 101, когда он был не подключен. Если сделать экспорт, то получится вот такая запись:
INSERT INTO `cdr` (`id`, `calldate`, `clid`, `src`, `dst`, `dcontext`, `channel`, `dstchannel`, `lastapp`, `lastdata`, `duration`, `billsec`, `disposition`, `amaflags`, `accountcode`, `uniqueid`, `userfield`) VALUES (1, '2020-02-27 19:08:17', '\"Number 100\" <100>', '100', 'num-not-connected', 'call-out', 'SIP/100-0000000e', '', 'Dial', 'SIP/101,15', 6, 4, 'ANSWERED', 3, '', '1582819697.14', '');
Для удобства дальнейшей работы, я настроил web сервер на базе apache. Не буду на этом подробно останавливаться. Чтобы двигаться дальше нам нужен классический web сервер на базе php. Для удобства рекомендую сразу настроить phpmyadmin или любой другой клиент к mysql. Если привыкли работать с mysql через консоль, можете обойтись без него.
Источник:https://serveradmin.ru/nastroyka-servera-telefonii-asterisk-s-nulya/