- PVSM.RU - https://www.pvsm.ru -

Голосовой автоинформатор даты и времени, приятным женским голосом, русским языком, на базе asterisk? Легко

В преддверии выходных не чем себя занять, так как по регламенту не позволены грандиозные настройки? На старом, заброшенном сервере запылился asterisk? Абоненту нечем тестировать телефонную линию? Для тех, кому не с кем поговорить и для тех, кто потерялся во времени.

Голосовой автоинформатор даты и времени, приятным женским голосом, русским языком, на базе asterisk? Легко

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

Текущее время пятнадцать часов, двадцать одна минута, двадцать секунд. Сегодня среда, пятнадцатое октября.

или

Текущее время один час, тридцать пять минут, десять секунд. Сегодня четверг, шестнадцатое октября.

Для простоты и прозрачности внедрения мы не будем пользоваться AGI и попросим железную леди сообщать нам дату и время, по большому счету, поработав лишь с dialplan`ом и say.conf`ом. И если ваш asterisk до сих пор не говорит по-русски — не беда, этому мы его научим. Кому стало интересно, добро пожаловать под хабракат.

Который час?

Думаю, не стоит даже упоминать, что ваш сервер должен знать точное время, опираясь, например, на NTP.

В двух словах о настройке NTP

Время на серверах имеет свойство рассинхронизироваться, если его не подводить. Пакет ntp потребуется в любом случае. Для себя я выбрал первый вариант с ntpd, так как другие мои сервера подводят время внутри сети.

#yum -y install ntp

Вариант №1. Добавляем сервера по вкусу [1], в зависимости от региона, в котором находится asterisk. Чем быстрее отклик от сервера времени, тем точнее оно выставится, при прочих равных. Я выберу российский пул.

#grep "^server" /etc/ntp.conf
server 0.ru.pool.ntp.org
server 1.ru.pool.ntp.org
server 2.ru.pool.ntp.org
server 3.ru.pool.ntp.org

#chkconfig ntpd on
#service ntpd start

Проверяем, что демон слушает нужные порты:

#netstat -putln | grep ntpd

Если есть желание раздавать время своим серверам, не забудьте проверить файервол, порт UDP/123. Правило должно встать до завершающего REJECT`а. Не забудьте сохранить.

#iptables -nL --line-numbers
#iptables -I INPUT 4 -s 10.0.0.0/255.0.0.0 -p udp --dport 123 -j ACCEPT
#service iptables save

Вариант №2. Для разовой корректировки подойдет утилита ntpdate, входящая в тот же пакет ntp. Такой вариант годен, если раздача времени с сервера не планируется.

#ntpdate ru.pool.ntp.org
9 Oct 17:08:41 ntpdate[32744]: adjust time server 85.21.78.8 offset -0.183259 sec

Можно добавить в cron:

#echo -e "n47 */1  *  *  * root /usr/sbin/ntpdate pool.ntp.org > /dev/nulln" >> /etc/crontab

Обучим леди русскому языку

Далее предположим, что asterisk установлен, но если это не так, то есть [2] много [3]материалов [4] на эту тему. Так же будем считать, что первоначальная настройка хотя бы одного SIP-телефона или софтфона уже произведена.

Пакет русских звуков

Для Asterisk 1.4, если добавить languageprefix=yes в asterisk.conf, структура звуковых каталогов будет как в более новых версиях по умолчанию.
Стандартный каталог: /var/lib/asterisk/ в котором подпапки, зависящие от двух «xx» букв ISO кода страны (ru, nl, fr, de, it, pt, es ...)

        sounds/xx
        sounds/xx/digits
        sounds/xx/letters
        sounds/xx/phonetic

Создадим каталог, если его нет

 #mkdir /var/lib/asterisk/sounds/ru/

Загружаем русские звуки:

 #wget -O asterisk-sounds-additional-master.zip https://github.com/pbxware/asterisk-sounds-additional/archive/master.zip
 #wget -O asterisk-sounds-master.zip https://github.com/pbxware/asterisk-sounds/archive/master.zip

Распаковываем:

 #unzip asterisk-sounds-additional-master.zip
 #unzip asterisk-sounds-master.zip

Копируем на свое место:

 #cp -R ./asterisk-sounds-additional-master/* /var/lib/asterisk/sounds/ru/
 #cp -R ./asterisk-sounds-master/* /var/lib/asterisk/sounds/ru/

Посмотреть, какие фразы записаны можно в следующих файлах:

 #less ./asterisk-sounds-additional-master/additional-sounds-ru.txt
 #less ./asterisk-sounds-master/core-sounds-ru.txt

sip.conf

Укажем asterisk`у, использовать русский язык для SIP, добавив language=ru в [general]:

#cat /etc/asterisk/sip.conf
[general]
language=ru

Применяем настройки:
#asterisk -rx "sip reload"

say.conf

#cat /etc/asterisk/say.conf

[ru-base](!)
    _[n]um:0X		=> num:${SAY:1}

    _[n]um:X 		=> digits/${SAY}
    _[n]um:[1-2]f	=> digits/${SAY:0:1}f
    _[n]um:[3-9]f	=> digits/${SAY:0:1}

    ; Tens
    _[n]um:1X		=> digits/${SAY:0:2}
    _[n]um:1Xf		=> digits/${SAY:0:2}

    _[n]um:[2-9]0       => digits/${SAY:0:2}
    _[n]um:[2-9]0f      => digits/${SAY:0:2}

    _[n]um:[2-9][1-2]   => digits/${SAY:0:1}0, num:${SAY:1}
    _[n]um:[2-9][1-2]f  => digits/${SAY:0:1}0, num:${SAY:1}

    _[n]um:[2-9][3-9]   => digits/${SAY:0:1}0, num:${SAY:1}
    _[n]um:[2-9][3-9]f  => digits/${SAY:0:1}0, num:${SAY:1}


    ; Hundreds
    _[n]um:0XX		=>	num:${SAY:1}
    _[n]um:0XXf		=>	num:${SAY:1}

    _[n]um:[1-9]00	=>	digits/${SAY:0:1}00
    _[n]um:[1-9]00f	=>	digits/${SAY:0:1}00

    _[n]um:XXX		=>	num:${SAY:0:1}00, num:${SAY:1}
    _[n]um:XXXf		=>	num:${SAY:0:1}00, num:${SAY:1}


    ; enumeration
    _e[n]um:X			=>	digits/h-${SAY}
    _e[n]um:X[n]		=>	digits/h-${SAY}
    _e[n]um:0X			=>	enum:${SAY:1}
    _e[n]um:0X[n]		=>	enum:${SAY:1}
    _e[n]um:1X			=>	digits/h-${SAY}
    _e[n]um:1X[n]		=>	digits/h-${SAY}
    _e[n]um:[2-9]0		=>	digits/h-${SAY}
    _e[n]um:[2-9]0[n]		=>	digits/h-${SAY}
    _e[n]um:[2-9][1-9]		=>	num:${SAY:0:1}0, digits/h-${SAY:1}
    _e[n]um:[2-9][1-9][n]	=>	num:${SAY:0:1}0, digits/h-${SAY:1}
    _e[n]um:[1-9]00		=>	digits/h-${SAY}
    _e[n]um:[1-9]00[n]		=>	digits/h-${SAY}
    _e[n]um:[1-9]XX		=>	num:${SAY:0:1}00, enum:${SAY:1}
    _e[n]um:[1-9]XX[n]		=>	num:${SAY:0:1}00, enum:${SAY:1}



[ru](ru-base)

    _chas:0		=>	num:${SAY}, digits/hours
    _chas:1		=>	digits/${SAY}, digits/hour
    _chas:[2-4]		=>	num:${SAY}, digits/hours-a
    _chas:[5-9]		=>	num:${SAY}, digits/hours
    _chas:0X		=>	chas:${SAY:1}
    _chas:1X		=>	num:${SAY}, digits/hours
    _chas:20		=>	num:${SAY}, digits/hours
    _chas:2[1-4]	=>	num:${SAY:0:1}0, chas:${SAY:1}

    _mi[n]uta:0		=>	num:${SAY}, digits/minutes
    _mi[n]uta:1		=>	digits/1f, digits/minute
    _mi[n]uta:2		=>	digits/2f, digits/minutes-i
    _mi[n]uta:[3-4]	=>	num:${SAY}, digits/minutes-i
    _mi[n]uta:[5-9]	=>	num:${SAY}, digits/minutes
    _mi[n]uta:0X	=>	minuta:${SAY:1}
    _mi[n]uta:1X	=>	num:${SAY}, digits/minutes
    _mi[n]uta:[2-5]0	=>	num:${SAY}, digits/minutes
    _mi[n]uta:[2-5][1-9]	=>	num:${SAY:0:1}0, minuta:${SAY:1}

    _seku[n]da:0	=>	num:${SAY}, seconds
    _seku[n]da:[5-9]	=>	num:${SAY}, seconds
    _seku[n]da:0X	=>	sekunda:${SAY:1}
    _seku[n]da:1X	=>	num:${SAY}, seconds
    _seku[n]da:[2-5]0	=>	num:${SAY}, seconds

    _dayofweek:[0-6]	=>	digits/day-${SAY} 

    _dayofmo[n]th:X	=>	enum:${SAY}n
    _dayofmo[n]th:XX	=>	enum:${SAY}n

    _mo[n]th:X		=>	digits/mon-$[${SAY} - 1]
    _mo[n]th:XX		=>	digits/mon-$[${SAY} - 1]

Применяем настройки:
#asterisk -rx "module reload app_playback.so"

Контекст [ru-base] в say.conf имеет завершающий (!) восклицательный знак в скобках означает, что это шаблон, который мы в дальнейшем включаем в [ru]

Попробуем разобрать одно правило. Первое, на что стоит обратить внимание, это символы X Z N. Они интерпретируются asterisk`ом как специальные и если эти литеры фигурируют в названии правила чтения, их следует взять в квадратные скобки, например mo[n]th.

_mo[n]th:XX		=>	digits/mon-$[${SAY} - 1]

Синтаксис достаточно прост и правило совпадает, если входные данные XX — две любые цифры. Проигрываем файл digits/mon-(XX-1), где (XX-1) это арифметическая операция. При X=02 (да, «переваривает» даже такие цифры, что нам очень поможет), 02-1=1, digits/mon-1: «Февраля».

Отдельно стоит упомянуть секунды. Во-первых, в записанных фразах есть только единственная запись: «секунд». Это значит, что на вход этой функции должны приходить округленные данные, например 0, 10, 20, и так далее. А во-вторых, по мнению автора, это облегчит восприятие полученной информации.

extensions.conf

#cat /etc/asterisk/extensions.conf

[my_regular_context]  ; Там где живет ваш SIP абонент/телефон.
exten => 100,1,Goto(informer_100,s,1)

[informer_100]
exten => s,1,Set(FreezeEPOCH=$[${EPOCH} + 15]) ; Добавляем 15 секунд к unixtime.
 same => n,Set(TimeNow=${STRFTIME(${FreezeEPOCH},,%Y%m%d%H%M.%S-%w-%j)})
 same => n,Playback(silence/1&at-tone-time-exactly) ;Тишина 1 секунда + Текущее время
 same => n,Playback(chas:${TimeNow:8:2},say) ; десять + часов
 same => n,Playback(minuta:${TimeNow:10:2},say) ; сорок + одна + минута
 same => n,Playback(sekunda:${TimeNow:13:1}0,say) ; двадцать + секунд
 same => n,Playback(silence/1&digits/today) ; тишина 1 секунда + сегодня
 same => n,Playback(dayofweek:${TimeNow:16:1},say) ; четверг
 same => n,Playback(dayofmonth:${TimeNow:6:2},say) ; шестнадцатое
 same => n,Playback(month:${TimeNow:4:2},say) ; октября
 same => n,Playback(silence/1&beep) ; тишина 1 секунда + короткий гудок
 same => n,Hangup()

Применяем настройки:
#asterisk -rx "dialplan reload"

same => n,Set(FreezeEPOCH=$[${EPOCH} + 15])

В контексте [informer_100] стоит объяснить строчку, где мы в переменной FreezeEPOCH добавляем 15 секунд к unixtime. Сделано это для компенсации времени, потраченного на проигрывание файлов, предшествующих секундам.

Далее мы формируем необходимый нам формат даты в переменной TimeNow. Она содержит данные в виде: 201410160043.34-4-289. При чтении мы выдергиваем из «массива» необходимые числа. Они всегда на своих местах и извлечение не составит труда. Более подробно о форматах можно посмотреть в #man strftime.

Про работу с переменными asterisk можно ознакомиться под спойлером

Полный синтаксис переменной ${AnyVariable:x:y}, где x — начальное положение, а y — количество цифр, которое должно быть возвращено. Пусть задана строка:
201410160043.34-4-289

Используя конструкцию ${AnyVariable:x:y}, можно извлечь следующие данные:

${AnyVariable:0:4} — будет возвращена строка 2014. Пропустить ноль символов слева и взять четыре символа.
${AnyVariable:4:8} — будет возвращена строка 10160043.
${AnyVariable:-3:3} — строка будет начинаться с третьего символа, считая с конца и включает три символа, что даст 289.
${AnyVariable:2} — если количество цифр, которое должно быть возвращено, не задано, будет возвращена вся оставшаяся строка, получим 1410160043.34-4-289.

Исходя из say.conf, секунды у нас должны округляться. Из двухзначного формата секунд мы выбираем первую цифру и добавляем к ней ноль: ${TimeNow:13:1}0

Возможно, кто-то из читателей захочет самостоятельно сформировать правила, например, для прочтения рублей. Объема готовых примеров должно быть достаточно, чтобы справиться с этой задачей. А для проверки произношения можно воспользоваться нижеприведенным dialplan`ом.

dialplan для перебора цифр/порядковых номеров/чего-либо

exten => 101,1,Set(Number=0) ; от нуля
	same=>n(start),playback(enum:0${Number}n,say) ; читать enum с предшествующим нулем
	same=>n,Set(Number=$[ ${Number} + 1  ]) ; шаг в единицу
	same=>n,GotoIf($[${Number} <= 9 ]?start) ; до девяти
	same=>n,Hangup()

Прослушать готовый результат в живую можно по телефону:

Голосовой автоинформатор даты и времени, приятным женским голосом, русским языком, на базе asterisk? Легко

Автор: xBrowser

Источник [5]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/ip-telefoniya/71902

Ссылки в тексте:

[1] по вкусу: http://support.ntp.org/bin/view/Servers/NTPPoolServers

[2] есть: https://wiki.asterisk.org/wiki/display/AST/Installing+Asterisk

[3] много: http://habrahabr.ru/post/171163/

[4] материалов: http://voxlink.ru/knowledge-base/asterisk-installation/

[5] Источник: http://habrahabr.ru/post/240649/