Pull to refresh

Google translate+Asterisk IVR

Reading time 7 min
Views 34K
Долго думал в какой блог запостить и решил, что здесь ему наиболее подходящее место. Хотя бы потому, что основная идея топика «sh — может все».

В этом топике задали интересную тему — реализовать IVR для * с использованием синтезатора из Google Translate.

Я в общем то даже не планировал этим заниматься, но мне стало интересно.


И первое чем я занялся я полез выяснять как же разговаривает Гугл. Говорит хорошо, но только 100 символов. Впрочем этого вполне достаточно для создания IVR. Довольный первым результатом я пустился на поиски того как бы этот голосок подрезать. Недолгие поиски вывели меня на вариант translate.google.com/translate_tts?q=Текст&tl=ru
Ткнулся с ним в браузер и получил mp3 с произнесенным текстом. Еще больше вдохновленный я засунул эту строчку в wget.

[utfadm@SIP:/var/lib/asterisk]> wget "http://translate.google.com/translate_tts?q=текст&tl=ru"
--2011-12-01 13:24:53-- translate.google.com/translate_tts?q=%D1%82%D0%B5%D0%BA%D1%81%D1%82&tl=ru
Распознаётся translate.google.com (translate.google.com)... 173.194.32.225, 173.194.32.234, 173.194.32.235, ...
Подключение к translate.google.com (translate.google.com)|173.194.32.225|:80... соединение установлено.
HTTP-запрос отправлен. Ожидание ответа... 403 Forbidden
2011-12-01 13:24:53 ОШИБКА 403: Forbidden.

--2011-12-01 13:24:53-- translate.google.com/translate_tts?q=%D1%82%D0%B5%D0%BA%D1%81%D1%82&tl=ru
Повторное использование соединения с translate.google.com:80.
HTTP-запрос отправлен. Ожидание ответа... 403 Forbidden
2011-12-01 13:24:53 ОШИБКА 403: Forbidden.


Тут то и ждал меня первый облом. Впрочем немного поразмыслив подумалось, что в корпорации добра люди не глупые и так просто отдавать wget mp3шечки они не будут. Но ведь браузеру отдают…

Значит замаскируемся под браузер.

[utfadm@SIP:/tmp]> wget -U "Lynx 1.2.3.4" "http://translate.google.com/translate_tts?q=текст&tl=ru"
--2011-12-01 13:27:22-- translate.google.com/translate_tts?q=%D1%82%D0%B5%D0%BA%D1%81%D1%82&tl=ru
Распознаётся translate.google.com (translate.google.com)... 74.125.232.1, 74.125.232.10, 74.125.232.11, ...
Подключение к translate.google.com (translate.google.com)|74.125.232.1|:80... соединение установлено.
HTTP-запрос отправлен. Ожидание ответа... 200 OK
Длина: 0 [audio/mpeg]
Сохранение в каталог: ««translate_tts?q=\321%82ек\321%81\321%82&tl=ru»».

[ <=> ] 0 --.-K/s за 0s

2011-12-01 13:27:22 (0,00 B/s) - «translate_tts?q=\321%82ек\321%81\321%82&tl=ru» saved [0/0]


Хм… длинна файла получилась нулевой. А если так

[utfadm@SIP:/tmp]> wget -U "Lynx 1.2.3.4" "http://translate.google.com/translate_tts?q=text&tl=ru"
--2011-12-01 13:29:59-- translate.google.com/translate_tts?q=text&tl=ru
Распознаётся translate.google.com (translate.google.com)... 74.125.232.2, 74.125.232.11, 74.125.232.12, ...
Подключение к translate.google.com (translate.google.com)|74.125.232.2|:80... соединение установлено.
HTTP-запрос отправлен. Ожидание ответа... 200 OK
Длина: 4421 (4,3K) [audio/mpeg]
Сохранение в каталог: ««translate_tts?q=text&tl=ru»».

100%[===================================================================================================================>] 4 421 --.-K/s за 0s

2011-12-01 13:29:59 (95,5 MB/s) - «translate_tts?q=text&tl=ru» saved [4421/4421]

А так работает…

Думаем, думаем, думаем…
Может быть русские символы приходящие от lynx не принимаются за русские?
Тогда сменим user-agent на тот с которым русские буквы точно работают.

[utfadm@SIP:/tmp]> /usr/local/bin/wget -U "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5" "http://translate.google.com/translate_tts?q=текст&tl=ru"
--2011-12-01 13:32:27-- translate.google.com/translate_tts?q=%D1%82%D0%B5%D0%BA%D1%81%D1%82&tl=ru
Распознаётся translate.google.com (translate.google.com)... 173.194.32.225, 173.194.32.234, 173.194.32.235, ...
Подключение к translate.google.com (translate.google.com)|173.194.32.225|:80... соединение установлено.
HTTP-запрос отправлен. Ожидание ответа... 200 OK
Длина: 4421 (4,3K) [audio/mpeg]
Сохранение в каталог: ««translate_tts?q=\321%82ек\321%81\321%82&tl=ru.1»».

100%[===================================================================================================================>] 4 421 --.-K/s за 0s

2011-12-01 13:32:27 (103 MB/s) - «translate_tts?q=\321%82ек\321%81\321%82&tl=ru.1» saved [4421/4421]


О, уже лучше… только название файла какое то корявеькое
translate_tts?q=?%82ек?%81?%82&tl=ru.1
в его исправлении нам поможет ключ -О, и имя будет устанавливаться какое нужно.

Так, теперь когда мы научились получать файлы с голосом, надо научить это делать *.

Для этого напишем небольшой скрипт
#!/bin/sh
`ls /var/lib/asterisk/festivalcache/$2.gsm`
if [ $? -eq 1 ]; then
NAME=/var/lib/asterisk/festivalcache/$2
/usr/local/bin/wget -U "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5" "http://translate.google.com/translate_tts?q=$1&tl=ru" -O $NAME.mp3
/usr/local/bin/mpg123 -w "$NAME.wav" "$NAME.mp3"
echo "Converting from wav to gsm"
/usr/local/bin/sox -t wav "$NAME.wav" -r 8000 -c1 -t gsm "$NAME.gsm"
rm $NAME.mp3
rm $NAME.wav
fi

Пробежимся по нему.
С первой строкой все и так понятно.
Второй проверяем наличие файла, и если он уже существует то на этом скрипт и заканчивает свою работу.
Если же нет, то устанавливаем имя файла из второго параметра запуска и полного пути к нему.
Кидаем гуглу запрос с текстом из первого параметра запуска для сохранения файла в mp3.
Потом гоним его в wav, потом в gsm.
Удаляем промежуточные файлы.

На выходе получаем файл формата gsm который чудесно умеет проигрывать *.

Ну и собственно пишем в диалплан что нибудь вроде:
exten => 227,1,Set(home=/var/lib/asterisk/festivalcache)
exten => 227,2,Wait(1)
exten => 227,n,System(/bin/sh /var/lib/asterisk/tts.sh ". Здравствуйте" "${EXTEN}.${PRIORITY}")
exten => 227,n,Playback(${home}/${EXTEN}.$[${PRIORITY} - 1])
exten => 227,n,Set(tic=${STRFTIME(${EPOCH},,%Y%m%d)}))
exten => 227,n,System(/bin/sh /var/lib/asterisk/dt.sh 1 "${EXTEN}.${tic}")
exten => 227,n,Playback(${home}/date/${EXTEN}.${tic})
exten => 227,n,System(/bin/sh /var/lib/asterisk/tts.sh ". Это меню сформированно при помощи Гугл переводчика" "${EXTEN}.${PRIORITY}")
exten => 227,n,Playback(${home}/${EXTEN}.$[${PRIORITY} - 1])
exten => 227,n,System(/bin/sh /var/lib/asterisk/tts.sh ". Досвидания." "${EXTEN}.${PRIORITY}")
exten => 227,n,Playback(${home}/${EXTEN}.$[${PRIORITY} - 1])
exten => 227,n,Set(tic=${STRFTIME(${EPOCH},,%H%M%S)})
exten => 227,n,System(/bin/sh /var/lib/asterisk/dt.sh 2 "${tic}")
exten => 227,n,Playback(${home}/time/${tic})
exten => 227,n,Hangup()


Таким образом при работе скрипта будут сформированы и проиграны файлы 227.3.gsm, 227.8.gsm, 227.10.gsm, и еще два о которых чуть позже. перечисленные файлы буду сгенерированы один раз, потому, что, как мы помним скрипт не исполняется если файл уже существует. Мне кажется что даже 50-60 однажды сгенерированных фраз гугл не обременят, а нам дадут полноценное меню.

Те два файла о которых я обещал рассказать позже представляют из себя текущие дату и время. Генерируются они и вызываются строками
exten => 227,n,Set(tic=${STRFTIME(${EPOCH},,%Y%m%d)}))
exten => 227,n,System(/bin/sh /var/lib/asterisk/dt.sh 1 "${EXTEN}.${tic}")
exten => 227,n,Playback(${home}/date/${EXTEN}.${tic})


и
exten => 227,n,Set(tic=${STRFTIME(${EPOCH},,%H%M%S)})
exten => 227,n,System(/bin/sh /var/lib/asterisk/dt.sh 2 "${tic}")
exten => 227,n,Playback(${home}/time/${tic})

соответсвенно.
Как видно из вызова обращаются они к другому скрипту. Это обертка для уже рассмотренного скрипта обращения к гуглу за голосом. Выглядит она следующим образом
#!/bin/sh
if [ $1 -eq 1 ]; then
q=`date +"Сегодня %d.%m.%Y года"`
n=date/$2
fi
if [ $1 -eq 2 ]; then
q=`date +"Точное время %H:%M:%S"`
n=time/$2
fi
echo "$q"
/var/lib/asterisk/tts.sh "$q" $n

Все довольно очевидно. Первый параметр определяет получаем ли мы дату или время, второй имя файла. Даты ложатся в папку date время в time. Имя файла нам передает *. tic=${STRFTIME(${EPOCH},,%Y%m%d)}) — это годмесяцдень) и tic=${STRFTIME(${EPOCH},,%H%M%S)} — часминутасекунда. Таким образом если долго не чистить папку time то можно накопить все возможные комбинации.

Такая вот простенькая оберточка.
Для генерации коротких фраз любого формата надо всего лишь написать обертку для первого скрипта. Простенько и со вкусом.

Но у меня была еще задумка читать файлы, но ведь в файле может быть больше 100 символов. Значит придется дробить на несколько запросов. Специфика файлов которые мне необходимо читать такова, что в них много строк, но все они меньше 100 символов. Поэтому я накидал следующий скрипт:
[root@SIP:/var/lib/asterisk]# cat ttsb.sh
#!/bin/sh
Source=/var/lib/asterisk/source
i=0
splitted=''
NAME=/var/lib/asterisk/festivalcache/$2
`ls /var/lib/asterisk/festivalcache/$2.gsm`
if [ $? -eq 1 ]; then
for str in `cat $Source/$1`
do
i=`expr $i + 1`
WORKNAME=/var/lib/asterisk/festivalcache/$2.work.$i.mp3
splitted="$splitted $WORKNAME"
#echo $WORKNAME
#echo $str
#echo SP: $splitted
/usr/local/bin/wget -U "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5" "http://translate.google.com/translate_tts?q=$str&tl=ru" -O $WORKNAME
done
/usr/local/bin/mpg123 -w "$NAME.wav" $splitted
echo "Converting from wav to gsm"
/usr/local/bin/sox -t wav "$NAME.wav" -r 8000 -c1 -t gsm "$NAME.gsm"
rm $splitted
rm $NAME.wav
fi

Ну тут все тоже достаточно очевидно. Берем файл и каждую его строку скармливаем гуглу, потом все mp3шки склеиваем в один wav, его конвертируем в gsm и удаляем промежуточные файлы. Слышна небольшая пауза, так что хорошо если логически строки подразумевают паузу между их произношением.

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

В планах наладить сфинкс (видел проект русских грамматик с точностью 96%), результат обработки скармливать гуглу, переводить на другой язык и произносить гуглом же. Пока правда не знаю зачем.
Tags:
Hubs:
+67
Comments 17
Comments Comments 17

Articles