...

понедельник, 17 июня 2013 г.

[Из песочницы] Сервис автоматизированной отправки факсов с помощью Asterisk

Имеем некий web-сервис, которому необходимо отправлять очень много факсов.

Можно использовать сторонние сервисы, которые специализируются на этом.

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

Используя свой сервис, мы будем платить повременно за голосовой трафик.

Сервис будет получать запрос на отправку факса и рапортовать нам о результатах.

Использовать будем Asterisk, может он не самый производительный, но хорошо известный.

В нашей конфигурации мы будем использовать готовую сборку Elastix, т.к. она работает стабильнее, если верить Klistrod ( Битва титанов FreeSwitch vs. Asterisk — Тест производительности).

Так же плюсом является наличие Apache и php. Доставлять пакеты не придется.


В Asterisk есть несколько способов отправки факсов: голосом через G.711 и с использованием T.38.

Второй вариант предпочтительнее, т.к. вероятность доставки выше.


Так же есть 2 реализации отправки факсов:

1. Spandsp OpenSource проект

2. Digium Fax for Asterisk

Коммерческая реализация от авторов астериска, 1 конкурентная лицензия бесплатная.

Подробности установки( docs.digium.com/FAX/fax_for_asterisk_admin_manual.pdf )

Spandsp уже включена в поставку Elastix.


Логически систему можно разбить на 3 части.

1.Прием запросов

2.Ротация факсов

3.Отправка факса и рапорт о результатах.


Прием запросов на отправку.



Инициация отправки факса будет происходить посредством перемещения .call файла в папку выполения астериска.

В call файле находятся все необходимые Asteriskу параметры для отправки.

faxsend.call



Callerid:"FaxSender"<1111>
Maxretries:maxRetries
Waittime:300
Context:faxsend-t38
Extension:faxout
RetryTime:50
Priority:1
SetVar: T38CALL=1
Set:RETURNURL={returnUrl}
Set:TAGLINE=Fax from CompanyName
Set:RECEIVER=Number Of Receiver
Set:FAX_ID={faxId}
Set:TIFF_2_SEND={faxId}.tif';


Создаем скрипт который будет принимать параметры и файл для отправки. Файл для отправки будем получать в PDF.

Параметры на которые, думаю стоит обратить внимание:

maxRetries — количество попыток дозвона.

faxid – идентификатор факса для которого будет возвращаться статус.

returnUrl – адрес по которому будет возвращаться результат отправки.


index.php



<?php
define( "STATUS_SUCCESS", "success" );
define( "STATUS_ERROR", "fail" );
$pdfLocation = "//var//tmp//faxes//";
function sendStatus( $statusKind )
{
header('Content-type: application/json');
exit( "{\"status\": \"" . $statusKind . "\"}" );
}

$fax = $_REQUEST['fax'];
$faxId = $_REQUEST['faxId'];
$maxRetries = isset( $_REQUEST['maxRetries'] ) ? $_REQUEST['maxRetries']
: 0;
$returnUrl = $_REQUEST['returnUrl'];
$pdf = $faxId . '.pdf';

if ($fax == '' || $returnUrl == '' || $faxId == ''
)
{
sendStatus( STATUS_ERROR );
}

if(!move_uploaded_file($_FILES['file']['tmp_name'], $pdfLocation .
$pdf))
{
sendStatus( STATUS_ERROR );
}

$callFileBody = 'Channel:SIP/trunkname/{fax}
Callerid:"FaxSender"<1111>
Maxretries:{maxRetries}
Waittime:300
Context:faxsend-t38
Extension:faxout
RetryTime:50
Priority:1
SetVar: T38CALL=1
Set:RETURNURL={returnUrl}
Set:TAGLINE=Fax from Company
Set:RECEIVER={fax}
Set:FAX_ID={faxId}
Set:TIFF_2_SEND={faxId}.tif';

$callFileBody = str_replace("{fax}", $fax, $callFileBody);
$callFileBody = str_replace("{faxId}", $faxId, $callFileBody);
$callFileBody = str_replace("{maxRetries}", $maxRetries, $callFileBody);
$callFileBody = str_replace("{returnUrl}", $returnUrl, $callFileBody);

$callFilename = $faxId . ".call";

file_put_contents($pdfLocation . $callFilename, $callFileBody);

if (!file_exists($pdfLocation . $callFilename))
{
sendStatus( STATUS_ERROR );
}

sendStatus( STATUS_SUCCESS );

?>



Таким образом в указанной папке pdfLocation появляется 2 файла: .call и .pdf.

Данный скрипт можно поместить в какую-нибудь из подпапок в /var/www/html

Я создал папку faxservice.

Таким образом наш сервис доступен по адресу http://serveradress/faxservice

По-умолчанию Elastix перенаправляет все запросы http на https. Что бы не заморачиваться с сертефикатами я этот редирект отключил:


/etc/httpd/conf.d/elastix.conf



# Apache-level configuration for Elastix administration interface
Timeout 300
# Default apache configuration specifies greater limits than these
#MaxClients 150
#MaxRequestsPerChild 1000
# Default apache User and Group diretives MUST be commented out
# in order for these to take effect.
User asterisk
Group asterisk
<Directory "/var/www/html">
# Redirect administration interface to https
#RewriteEngine off
#RewriteCond %{HTTPS} off
#RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
</Directory>


Просто закомментировал реврайты.


Ротация факсов



Нам необходим мониторинг этой папки на предмет появления новых заданий, и после появления заданий, передачи в очередь на отправку в Asterisk. Так же необходимо конвертация нашего pdf в tif

Данный функционал мы реализуем с помощью bash скрипта:

faxrotate



#!/bin/bash
SOURCEDIR="/var/tmp/faxes/"
for file in `ls $SOURCEDIR*.pdf `;
do
callfile="${file/pdf/call}"
if [ ! -e "$callfile" ]
then
continue
fi
`gs -q -dNOPAUSE -dBATCH -sDEVICE=tiffg4 -sPAPERSIZE=letter -sOutputFile=${file/pdf/tif} $file`
mv ${file/pdf/call} /var/spool/asterisk/outgoing
rm $file
done




Подвешиваем выполнение данного скрипта каждую минуту от имени Asterisk.

«Отработанные» файлы tif будем удалять каждую ночь, хранить их будем 2 дня (на тот случай, если количество попыток будет большим, а промежутки между ними еще больше.):



/usr/bin/find /var/tmp/faxes -name "*tif" -mtime +2 –delete


Отправка факсов Asteriskом

Осталось теперь только научить Asterisk отправлять факсы через T.38 и рапортовать нам о результатах.

Для начала необходимо «научить» Elastix работать с T.38


Для этого в sip_general_custom.conf добавляем:



t38pt_udptl=yes


А в настройках транспорта не забываем добавить



transport=udp,udptl


Примерный вид настроек транка:



[trunkname]
username=username
type=friend
transport=udp,udptl
secret=password
qualify=yes
nat=yes
insecure=port,invite
host=voip.host.com
disalow=all
directmedia=yes
context=from-pstn
canreinvite=yes
allow=ulaw&alaw


Далее необходимо создать контекст, который будет вызываться из call файла

Его добавляем в


extensions_custom.conf



[faxsend-t38]
exten => faxout,1,Set(STARTTIME=${SHELL(date +%s)} )
exten => faxout,n,Wait(1)
exten => faxout,n,Playback(fax24,skip)
exten => faxout,n,Wait(1)
exten => faxout,n,NoOp(**** SENDING FAX ****)
; Set FAXOPTs
exten => faxout,n,NoOp(**** SETTING FAXOPT ****)
exten => faxout,n,Set(FAXFILE=${TIFF_2_SEND})
exten => faxout,n,Set(FAXOPT(ecm)=yes)
exten => faxout,n,Set(FAXOPT(headerinfo)=${TAGLINE})
exten => faxout,n,Set(FAXOPT(maxrate)=14400)
exten => faxout,n,Set(FAXOPT(minrate)=4800)
; Send the fax
exten => faxout,n,NoOp(**** SENDING FAX : ${FAXFILE} ****)
exten => faxout,n,SendFAX(/var/tmp/faxes/${FAXFILE},dfzs)

;Calculating Time of Sending
exten => faxout,n,Set(ENDTIME=${SHELL(date +%s)} )
exten => faxout,n,Set(TRANSFERTIME=${MATH(${ENDTIME}-${STARTTIME},int)})
;Actions after sending fax
exten => faxout,n,Set(NORMURL=${FAXOPT(error)})
exten => faxout,n,Set(STATUSMESSAGE=${REPLACE(NORMURL, ,+)})
exten => faxout,n,Set(FAXOPTRATE=${FAXOPT(rate)})
exten => faxout,n,Hangup

;Actions if no answer or busy
exten => failed,1,Set(FAXSTATUS=FAILED)
exten => failed,2,Set(STATUSMESSAGE=number+no+answer+or+busy)
exten => failed,3,Set(FAXOPTRATE=none)

exten => h,1,NoOP(------------------- FAX to ${EXTEN} with ${FAXSTATUS} -----------------)
exten => h,2,Set(CURLRESULT=${CURL(${RETURNURL}?fax=${RECEIVER}&faxId=${FAX_ID}&status=${FAXSTATUS}&message=${STATUSMESSAGE})})
exten => h,4,Set(LOGFAXOUT=${SHELL(echo "${STRFTIME(${EPOCH},,%d%m%Y-%H:%M:%S)} : ${FAX_ID} : ${RECEIVER} : ${FAXSTATUS} : ${STATUSMESSAGE} : ${TRANSFERTIME}s : ${FAXOPTRATE}" >> /var/log/asterisk/faxout.log)})
exten => h,3,NoOp(${RECEIVER}:${FAX_ID}:${FAXSTATUS}:${STATUSMESSAGE}:${FAXOPTRATE})


Данный контекст пытается дозвониться и отправить факс, при недозвоне он возвращает number+no+answer+or+busy.

При удачном дозвоне и попытке отправки возвращает статус и расшифровку статусного сообщения.

Эту информацию он возвращает по адресу RETURNURL из php скрипта.

Так же он пишет лог файл для дальнейших разборов полетов по каким-то спорным вопросам.

Для работы голосового приветствия необходимо записать файл в формате PCM Encoded, 16 Bits, at 8000Hz и поместить его в /var/lib/asterisk/sounds.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends: 'You Say What You Like, Because They Like What You Say' - http://www.medialens.org/index.php/alerts/alert-archive/alerts-2013/731-you-say-what-you-like-because-they-like-what-you-say.html


Комментариев нет:

Отправить комментарий