📜 ⬆️ ⬇️

Automated faxing service using Asterisk

We have a web service that needs to send a lot of faxes.
You can use third-party services that specialize in this.
But if there are a lot of faxes, then this translates into such a pretty penny. Therefore, we will create our service.
Using our service, we will pay on time for voice traffic.
The service will receive a request to send a fax and report to us about the results.

We will use Asterisk, maybe it is not the most productive, but well known.
In our configuration, we will use the ready-made Elastix assembly, since it works more stable if you believe Klistrod ( Battle Titans FreeSwitch vs. Asterisk - Performance Test ).
The advantage is the presence of Apache and php. It is not necessary to deliver packages.

Asterisk has several ways to send faxes: voice over G.711 and using T.38.
The second option is preferable, because the probability of delivery is higher.
')
There are also 2 implementations of sending faxes:
1. Spandsp OpenSource project
2. Digium Fax for Asterisk
Commercial implementation from the authors of the asterisk, 1 competitive license is free.
Installation details ( docs.digium.com/FAX/fax_for_asterisk_admin_manual.pdf )
Spandsp is already included in the Elastix distribution.

Logically, the system can be divided into 3 parts.
1. Receive requests
2. Rotation of faxes
3. Sending a fax and a report on the results.

Receive shipment requests.

Fax sending will be initiated by moving the .call file to the asterisk execution folder.
In the call file are all the necessary parameters for Asterisk to send.

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'; 


Create a script that will take the parameters and file to send. The file for sending will be received in PDF.
Parameters to which I think you should pay attention
maxRetries - the number of dialing attempts.
faxid - fax ID for which the status will be returned.
returnUrl - the address to which the result of the send will be returned.

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 ); ?> 


Thus, 2 files appear in the specified pdfLocation folder: .call and .pdf.
This script can be placed in any of the subfolders in / var / www / html
I created the faxservice folder.
Thus, our service is available at http: // serveradress / faxservice
By default, Elastix redirects all http requests to https. In order not to bother with the certificates, I disabled this redirect:

/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> 


Just commented rewright.

Fax rotation

We need to monitor this folder for the appearance of new tasks, and after the appearance of tasks, transfer to the queue to be sent to Asterisk. It is also necessary to convert our pdf to tif
We implement this functionality using the bash script:

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 

We suspend the execution of this script every minute on behalf of Asterisk.

We will delete the “spent” tif files every night, we will store them for 2 days (in case the number of attempts is large, and the intervals between them are even larger.):

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


Asterisk faxing


It only remains to teach Asterisk to send faxes via T.38 and report the results to us.
First you need to "teach" Elastix to work with T.38

To do this, add to sip_general_custom.conf:
 t38pt_udptl=yes 


Approximate view of trunk settings:

 [trunkname] username=username type=friend transport=udp 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 


Next, you need to create a context that will be called from the call file.
We add it to

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}) 


This context tries to call and send a fax, when it is not dialing, it returns number + no + answer + or + busy.
Upon successful dialing and attempting to send, it returns the status and decryption of the status message.
He returns this information to the address RETURNURL from the php script.
He also writes a log file for further debriefing on some controversial issues.
For voice greeting to work, you need to record a file in PCM Encoded format, 16 Bits, at 8000Hz and place it in / var / lib / asterisk / sounds.

Source: https://habr.com/ru/post/183584/


All Articles