Monday, October 20, 2008

Произносим числа словами, альтернатива SayNumber в Asterisk

Казалось бы, совсем детская задачка - имея на входе число, например, 42, получить на выходе строку символов - "сорок два". Вот и в Asterisk(программная АТС с открытым исходным кодом) есть встроенная функция SayNumber, которая "произносит"(проигрывает) число в канал. На самом деле, это ничем не отличается от простого преобразования числа в последовательность слов - Asterisk просто проигрывает звуковые файлы, которые имеют соответствующие словам названия.
Asterisk предоставляет простейшую функциональность по локализации звуковых файлов - вместо стандартной озвучки можно подставить свою, например русскую. И тогда 42 будет произноситься не как "fourty two", а как "сорок два". Однако, такая локализация не полная - при ближайшем рассмотрении можно заметить, что в русском языке числа произносятся совсем не так, как в английском. Например, 201 произносится как "двести один", а не как "два сотня один". Более того, в русском языке есть еще падежи, единственное/множественное число, мужской/женский/средний род. И когда нужно произнести что-то вроде "одна тысяча четыреста один рубль" или "две тысячи сорок пять рублей", встроенные средства совсем не подходят.
На voip-info.org даже есть ссылка на альтернативную реализацию SayNumber, которая настраивается через конфигурационные файлы(на voip-info.org неправильная ссылка, правильная на данный момент - альтернатива SayNumber от www.beronet.com ). Однако она реализована на С, в виде модуля, так что слабо портируема между версиями Asterisk: я не смог использовать ее в Asterisk 1.4.x. И тогда мне пришла в голову мысль написать свою альтернативную реализацию. В качестве требований выступили:

  • Возможность гибкой настройки(решение: использовать конфигурационные файлы)

  • Переносимость между версиями, удобство установки и использования(решение: использовать механизм AGI)

  • Элегантность (решение: использовать Python :) )
Не затягивая долго, покажу, что в итоге получилось: Исходный код.
Или без подсветки: Plain text.
Я старался комментировать и документировать код, чтобы он был прост и понятен. Также надеюсь, что он оказался достаточно элегантен (любые замечания или исправления приветствуются). Осталось только показать, как его использовать:
exten => _X.,n,AGI(pysaynumber.agi|42r|ru.conf)
, где pysaynumber.agi - название скрипта, 42r - что надо произнести, ru.conf - название конфигурационного файла.
Конфигурационный/ные файлы нужно положить рядом с pysaynumber.agi в agi-bin (его местоположение разнится в зависимости от места установки Asterisk)
Пример конфигурационного файла: здесь
Формат конфигурационного файла. Частично позаимствован из наработок www.beronet.com. Каждая строка состоит из двух частей, разделенных ":". Первая часть - регулярное выражение, без начального "^" и конечного "$"(они подставляются автоматически). Вторая часть - список действий, разделенных ";". Алгоритм прост - идем подряд по всем правилам и если находим match с номером, который мы желаем "произнести", то выполняем подряд все действия по соответствующему списку. Видов действий три. Если строка действия представляет собой конечное "слово" (например, rublei, 40), то мы просто "произносим" его. Если строка действия начинается с number, то это второй тип действия. Параметры указываются в скобках, вот так - (индекс|суффикс). При выполнении этого действия "произносится" конечное "слово", составленное из символа, который находится в исходном номере по индексу и суффикса. Третье действие - recursive. Параметры - (индекс[,индекс...]|суффикс). Запускает весь алгоритм рекурсивно, но уже для номера составленного из символов, которые берутся из исходного номера по индексам, и суффикса.
Приведенный мной код может использоваться не только для Asterisk(проверялось на Asterisk 1.4.18.1), а и для других нужд. Также для того, чтобы ускорить работу скрипта можно использовать FastAGI, тогда можно будет хранить таблицу уже проинициализированной и не запускать интерпретатор каждый раз заново.
UPD. Используйте FastAGI. По непонятной причине подобным образом написанные скрипты приводят к подвисанию Asterisk со 100% загрузкой процессора. С применением FastAGI таких проблем не наблюдалось.