- PVSM.RU - https://www.pvsm.ru -
Факс — это одна из тех вещей, которой многие желают скорейшей смерти. Тем не менее в регионах этот способ передачи информации по прежнему используется очень часто. Так и в нашей организации появилась необходимость по возможности упростить данный процесс. После изучения уже существующих здесь статей я пришел к выводу, что представленные решения не совсем подходят в моей ситуации. В частности, хотелось немного более интеллектуальную систему, чем просто основанную на call файлах. Такую, чтобы она могла перезванивать несколько раз в случае неудачной отправке. При этом пользователь должен видеть текущее состояние доставки. В совокупности с тем, что мне давно хотелось посмотреть на веб-разработку в целом и node.js в частности, было принято решение написать свой велосипед сервер исходящих факсов. Что из этого получилось можно увидеть под катом.
В первую очередь необходимо было настроить Asterisk так, чтобы он создавал события, отображающие продвижение факса в реально времени. Ниже приведен фрагмент плана набора Asterisk, который позволит нам делать это, а так же обрабатывать следующие ситуации:
Кроме этого план набора позволяет ограничить число одновременных исходящих факс-вызовов, чтобы случайно не занять все свободных SIP каналы.
[OutgoingFaxInit]
; Этот экстеншен используется для совершения исходящего звонка
exten => _X.,1,NoOp()
same => n,Set(GROUP()=faxout)
; Запоминаем количество одновременных факс-соединений в БД Asterisk,
; чтобы failed экстеншен мог отличать превышение одновременных от ситуации,
; когда соединение просто не удалось.
same => n,Set(DB(fax_group_count/${UUID})=${GROUP_COUNT(faxout)})
same => n,GotoIf($[${DB(fax_group_count/${UUID})}<=${MAX_PARALLELISM}]?call)
same => n,UserEvent(Fax,uuid: ${UUID},Status: CALL SUSPENDED)
same => n,HangUp()
same => n(call),Dial(Local/${EXTEN}@OutgoingCalls)
same => n,HangUp()
; Этот экстеншен будет выступать источником данных с нашей стороны
exten => router,1,NoOp()
same => n,Set(__UUID=${UUID})
same => n,Set(__DATA=${DATA})
same => n,Dial(Local/fax@OutgoingFax)
same => n,HangUp()
exten => failed,1,NoOp()
; В случае, если факс был прерван из-за превышения числа соединений UserEvent
; создаваться не будет
same => n,GotoIf($[${DB_DELETE(fax_group_count/${UUID})}<=${MAX_PARALLELISM}]?:end)
same => n,UserEvent(Fax,uuid: ${UUID},Status: CALL PICKUP FAILED)
same => n(end),HangUp()
[OutgoingFax]
exten => fax,1,NoOp()
same => n,UserEvent(Fax,uuid: ${UUID},Status: CALL PICKUP SUCCESS);
; Передача факса еще не началась. Запоминаем это.
same => n,Set(DB(fax_sendstatus/${UUID})=0)
same => n,Playback(autofax)
same => n,Set(FAXOPT(headerinfo)=Company)
same => n,Set(FAXOPT(localstationid)=XXX-XX-XX)
; Началась передача факса
same => n,Set(DB(fax_sendstatus/${UUID})=1)
same => n,SendFax(${DATA})
same => n,HangUp()
exten => h,1,NoOp()
; Если передача факса не была начата, то генерируем событие
; неудачной отправки
same => n,GotoIf($[${DB_DELETE(fax_sendstatus/${UUID})}]?sendstatus)
same => n,UserEvent(Fax,uuid: ${UUID},Status: FAX SEND FAILED)
same => n,Goto(end)
; Если мы начали передавать факс, то сообщаем данные из ${FAXOPT}
same => n(sendstatus),UserEvent(Fax,uuid: ${UUID},Status: FAX SEND ${FAXOPT(status)})
same => n(end),NoOp()
Основная часть нашего факс-сервера, как было отмечено выше, будет работать на node.js. С Asterisk мы будем взаимодействовать по AMI. Для полноценной работы клиенту будет достаточно прав на создание вызовов и чтения UserEvent'ов. Таким образом manager.conf будет иметь следующий вид:
[general]
enabled=yes
[FAX]
secret=password
read=user
write=originate
Для работы с AMI был выбран модуль nami [1]. В отличие аналог он подкупает достаточно большим функционалом из коробки. Есть уже готовые методы для работы с большей частью событий и генерации Action'ов. Стоит отметить, что у автора данного модуля есть реализации AMI интерфейсов и для других языков, кроме JS.
Общий механизм работы факс-сервера следующий:
Для реализации очередь и самой базы данных используется Redis [2]. Структура хранения данных следующая:
Для создания приятной глазу веб-формы был выбран twitter bootstrap. Отображение информации пользователю сделано через jQuery datatables [3]. Тут меня поджидала проблема с тем, что jQuery datatables не адаптирован к текущий версии bootstrap 3. К счастью на github был репозиторий [4]исправленной версии.
В конечном итоге получилось следующее:
Все основные настройки расположены в config.json:
{
"logLevel": "info",
"port": 80, // порт веб сервера
"FAX": {
"uploadDir": "/tmp/faxout", // каталог для загрузок
"storageDir": "/tmp/faxout", // каталог для хранения TIFF файлов факсов
"gsCommand": "gs", // команда вызова Ghostscript (им конвертируются входящие файлы)
"maxParallelism": 3, // максимально количество одновременных вызовов
"maxRetry": 5, // максимально количество повторных факсов
"retryInterval": 420, // интервал повтора факсов
"delayedProcessingInterval": 5 // интервал проверки очереди отложенных факсов
},
"AMI": { // данные для подключения к AMI
"host": "192.168.1.1",
"port": 5038,
"username": "FAX",
"secret": "password"
}
Получить исходный код можно на github [5]. Для работы необходимо добавить в план набора Asterisk описанный выше фрагмент а так же иметь на сервере redis и node.js. Надеюсь мой «Hello world» (он же факс-сервер) на node.js окажется вам полезным.
Автор: Infactum
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/news/51431
Ссылки в тексте:
[1] nami: http://marcelog.github.io/Nami/
[2] Redis: http://redis.io/
[3] jQuery datatables: http://datatables.net/
[4] репозиторий : https://github.com/Jowin/Datatables-Bootstrap3
[5] github: https://github.com/Infactum/faxserver
[6] Источник: http://habrahabr.ru/post/207080/
Нажмите здесь для печати.