понедельник, 4 июля 2016 г.

Использование PHPDAEMON в CentOS7

Подготовка к установке

До установки демона необходимо обновиться до php 5.6
Обновление PHP будет производиться из репозитория Remi, поэтому произведем его подключение.
# rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
Редактируем файл репозитария /etc/yum.repos.d/remi.repo
# nano /etc/yum.repos.d/remi.repo
В секции
[remi-php56]
ставим
enabled=1
Затем устанавливаем новую версию php:
# yum install php -y
# systemctl restart httpd
Проверить версию php можно так:
# php -v
PHP 5.6.23 (cli) (built: Jun 22 2016 08:56:52)

Далее нужно поставить свежую версию библиотеки libevent 2
# cd /usr/src/
# yum install gcc kernel-devel make ncurses-devel
# curl http://sourceforge.net/projects/levent/files/latest/download?source=files -L -o libevent2.tar.gz -w 'Last URL was: %{url_effective}'
# curl http://sourceforge.net/projects/tmux/files/latest/download?source=files -L -o tmux.tar.gz -w 'Last URL was: %{url_effective}'
# tar zxvf libevent2.tar.gz
# cd libevent-2.0.22-stable
# ./configure --prefix=/usr/local
Библиотеку устанавливаем в папку/usr/local
# make
# make install

Ставим необходимые в дальнейшем пакеты и модули php:
# yum install -y git gcc openssl-devel
# yum install php-pear
# yum install php-devel
# pecl install eio inotify
# pecl install http://pecl.php.net/get/event
На вопросы, задаваемые инсталлятором отвечаем так:
libevent installation prefix [/usr] : /usr/local
Include libevent's pthreads library and enable thread safety support in Event [no] : yes
На остальные вопросы можно ответить значением по умолчанию просто нажав Enter.

Для корректной работы модулей php event и eio необходим модуль sockets.
В системах RedHat/CentOS файлы конфигурации подгружаются в алфавитном порядке, поэтому назовем их z-event.ini и z-eio.ini соответственно и проведем подключение:
# echo "extension=event.so" > /etc/php.d/z-event.ini
# echo "extension=eio.so" > /etc/php.d/z-eio.ini

Установка phpdaemon

Готовим директорию, где будет работать демон phpdaemon и устанавливаемся:
#  mkdir /opt/phpdaemon
# cd /opt/phpdaemon
# git clone https://github.com/kakserpom/phpdaemon.git ./
Копируем конфигурационный файл из примера.  
# cp /opt/phpdaemon/conf/phpd.conf.example /opt/phpdaemon/conf/phpd.conf
Создаем символьную ссылку
# ln -s /opt/phpdaemon/bin/phpd /usr/bin/phpd

Устанавливаем временную зону в файле php.ini
Открываем файл:
# nano  /etc/php.ini
Прописываем в переменную date.timezone  - Московское время
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = Etc/GMT-3

Запуск phpdaemon

Запускаем демон таким образом:
# phpd start --verbose-tty=1
Опция --verbose-tty=1 указыват демону выводить журнал в консоль.
Нормальный вывод сейчас должен выглядеть так:
# phpd start --verbose-tty=1
[PHPD] Loaded config file: 'conf/conf.d/ExampleJabberBot.conf'
[PHPD] Loaded config file: 'conf/conf.d/FastCGI.conf'
[PHPD] Loaded config file: 'conf/conf.d/FlashpolicyServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/HTTPServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/IdentServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/SSL-sample.conf'
[PHPD] Loaded config file: 'conf/conf.d/WebSocketServer.conf'
[PHPD] Loaded config file: '/opt/phpdaemon/conf/phpd.conf'
#

В дальнейшем работаем с демоном так:
# phpd start Запуск демона
# phpd stop Остановка демона
# phpd reload, phpd restart Перезапуск демона
# phpd status Статус демона
# phpd fullstatus Статус демона подробный

Логи работы сервера будут складываться по умолчанию в файл /var/log/phpdaemon.log.

Добавляем phpdaemon в автозапуск. Для этого создаем сервис systemd.
# touch /etc/systemd/system/multi-user.target.wants/phpdaemonstart.service
# chmod 664 /etc/systemd/system/multi-user.target.wants/phpdaemonstart.service
# nano /etc/systemd/system/multi-user.target.wants/phpdaemonstart.service
Содержание файла:
[Unit]
Description=PHP DAEMON START
After=network.target httpd.target

[Service]
Type=forking
User=root
Group=root
ExecStart=/usr/bin/php -f /opt/phpdaemon/bin/phpd start &

[Install]
WantedBy=multi-user.target 
Рестартуем демон systemd:
# systemctl daemon-reload
Теперь запустить демон phpdaemon можно и так:
# systemctl start phpdaemonstart.service

Phpdaemon и серверы HTTP и WebSocket

Включаем websocket сервер phpdaemon
# nano /opt/phpdaemon/conf/conf.d/WebSocketServer.conf
Содержимое файла приводим к виду:
Pool:WebSocketServer {
        # you can redefine default settings here
        privileged;
        listen 'tcp://0.0.0.0';
        port 8021;
}

Включаем web-сервер
# nano /opt/phpdaemon/conf/conf.d/HTTPServer.conf
Содержимое файла приводим к виду:
Pool:HTTPServer {
        #privileged;
        # you can redefine default settings here
        enable 1;
        listen '0.0.0.0';
        port 8080;
        expose 1;
}

Перезапускаем демон:
# phpd stop
# phpd start --verbose-tty=1
Вывод загрузки должен быть такой:
# phpd start --verbose-tty=1
[PHPD] Loaded config file: 'conf/conf.d/ExampleJabberBot.conf'
[PHPD] Loaded config file: 'conf/conf.d/FastCGI.conf'
[PHPD] Loaded config file: 'conf/conf.d/FlashpolicyServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/HTTPServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/IdentServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/SSL-sample.conf'
[PHPD] Loaded config file: 'conf/conf.d/WebSocketServer.conf'
[PHPD] Loaded config file: '/opt/phpdaemon/conf/phpd.conf'
[PHPD] M#23145 \PHPDaemon\Core\Pool:HTTPServer up.
[PHPD] ClassFinder: 'HTTPServer' -> '\PHPDaemon\Servers\HTTP\Pool', you should change your code.
[PHPD] M#23145 \PHPDaemon\Core\Pool:WebSocketServer up.
[PHPD] ClassFinder: 'WebSocketServer' -> '\PHPDaemon\Servers\WebSocket\Pool', you should change your code.
[root@callpeg ~]# [PHPD] M#23145 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\WebSocket\Pool up.
[PHPD] W#23149 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23152 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23148 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23150 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23155 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23147 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23151 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23156 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
Проверяем, что демон стартовал:
# ps ax | grep phpd
23307 ?        SNs    0:00 phpd: master process
23308 ?        SN     0:00 phpd: IPC process
23309 ?        SNl    0:00 phpd: worker process
23310 ?        SNl    0:00 phpd: worker process
23311 ?        SNl    0:00 phpd: worker process
23312 ?        SNl    0:00 phpd: worker process
23313 ?        SNl    0:00 phpd: worker process
23315 ?        SNl    0:00 phpd: worker process
23317 ?        SNl    0:00 phpd: worker process
23318 ?        SNl    0:00 phpd: worker process
23326 pts/1    S+     0:00 grep --color=auto phpd
Проверяем, слушаются ли порты 8021 (websoket-сервер от phpdaemon) и 8080 (web-сервер от phpdaemon)
# netstat -ltupn | grep phpd
tcp        0      0 0.0.0.0:8080   0.0.0.0:*  LISTEN   19297/phpd: master
tcp        0      0 0.0.0.0:8021   0.0.0.0:*  LISTEN   19297/phpd: master
Для возможности прослушивания портов по сети, открываем эти порты в iptables
# nano /etc/sysconfig/iptables
-A INPUT -p tcp --dport 8021 -j ACCEPT
-A INPUT -p tcp --dport 8080 -j ACCEPT
# systemctl restart  iptables

Включим в конфигурационном файле phpd.conf работу двух приложений, идущих в комплекте с пакетом phpdaemon,
# nano /opt/phpdaemon/conf/phpd.conf
Приводим файл к виду
## Config file

user root;
group root;

#Логирорвание
logging false;
log-storage '/var/log/phpdaemon.log';

max-workers     8;
min-workers     8;
start-workers   8;
max-idle        0;
#add-include-path '/path/to/your/folder';

# apps routing
path '/opt/phpdaemon/conf/AppResolver.php';

ExampleWebSocket {
}

ServerStatus {
}

include conf.d/*.conf;
Здесь указывается, что демон будет работать из-под пользователя root.
Файл описания маршрутизации запросов будет находиться здесь: /opt/phpdaemon/conf/AppResolver.php
Включены приложения ExampleWebSocket и ServerStatus
Логирование выключено.

Все приложения демона должны быть размещены в папке /opt/phpdaemon/PHPDaemon/Applications/.
Приложение ServerStatus в этой папке уже есть, а приложение ExampleWebSocket нужно скопировать туда из папки примеров:
# cp /opt/phpdaemon/Examples/ExampleWebSocket.php /opt/phpdaemon/PHPDaemon/Applications/ExampleWebSocket.php
Что бы этот пример заработал необходимо в файл внести небольшие изменения:
1. В самом начале файла строку
namespace PHPDaemon\Examples;
заменить на:
namespace PHPDaemon\Applications;
2. Строку
ws = new WebSocket('ws://' + document.domain + ':8047/exampleApp');
заменить на 
ws = new WebSocket('ws://' + document.domain + ':8021/exampleApp');

Для корректной отработки запросов к web-серверу на созданные приложения, необходимо внести изменения в конфигурационный файл маршрутизации AppResolver.php
# nano /opt/phpdaemon/conf/AppResolver.php
Функция getRequestRoute в этом файле описывает какие приложения будут отрабатывать при поступлении запросов к серверу из браузера клиента.
К имеющемуся условию:
if (preg_match('~^/(WebSocketOverCOMET|Example.*)/?~', $req->attrs->server['DOCUMENT_URI'], $m)) {
      return $m[1];
}
Добавляем условие:
if (preg_match('~^/(ServerStatus)/?~', $req->attrs->server['DOCUMENT_URI'], $m)) {
       return 'ServerStatus';
}

Перезапускаем демон:
# phpd stop
# phpd start --verbose-tty=1
Лог запуска теперь будет такой:
# phpd start --verbose-tty=1
[PHPD] Loaded config file: 'conf/conf.d/ExampleJabberBot.conf'
[PHPD] Loaded config file: 'conf/conf.d/FastCGI.conf'
[PHPD] Loaded config file: 'conf/conf.d/FlashpolicyServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/HTTPServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/IdentServer.conf'
[PHPD] Loaded config file: 'conf/conf.d/SSL-sample.conf'
[PHPD] Loaded config file: 'conf/conf.d/WebSocketServer.conf'
[PHPD] Loaded config file: '/opt/phpdaemon/conf/phpd.conf'
[PHPD] M#23255 \PHPDaemon\Core\Pool:HTTPServer up.
[PHPD] ClassFinder: 'HTTPServer' -> '\PHPDaemon\Servers\HTTP\Pool', you should change your code.
[PHPD] M#23255 \PHPDaemon\Core\Pool:WebSocketServer up.
[PHPD] ClassFinder: 'WebSocketServer' -> '\PHPDaemon\Servers\WebSocket\Pool', you should change your code.
[PHPD] M#23255 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\WebSocket\Pool up.
[root@callpeg ~]# [PHPD] W#23258 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23258 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23258 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23257 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23257 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23257 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23261 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23261 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23261 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23259 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23259 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23259 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23262 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23260 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23262 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23262 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23260 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23260 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23263 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23263 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23263 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.
[PHPD] W#23264 \PHPDaemon\Applications\ExampleWebSocket up.
[PHPD] W#23264 \PHPDaemon\Applications\ServerStatus up.
[PHPD] W#23264 \PHPDaemon\Core\Pool:\PHPDaemon\Servers\HTTP\Pool up.

Теперь при обращении к серверу по адресу http://<ip-сервера>:8080/ServerStatus получим ответ о статусе сервера:


А при обращении к ресурсу http:// <ip-сервера>:8080/ExampleWebSocket  получим пример работы приложения на web-soket:


Первой кнопкой можно открыть сокет, второй кнопкой посылается запрос «ping» серверу, на который тот отвечает словом «pong». Третья кнопка служит для закрытия сокета.



В логах сервера phpdaemon, которые валять в консоль сервера мы увидим примерно следующее:
[PHPD] Notice: Undefined index: waitinit in /opt/phpdaemon/PHPDaemon/Applications/ServerStatusRequest.php:48
#1  PHPDaemon\Core\Daemon::errorHandler() called at [/opt/phpdaemon/PHPDaemon/Applications/ServerStatusRequest.php:48]
#2  PHPDaemon\Applications\ServerStatusRequest->run() called at [/opt/phpdaemon/PHPDaemon/Request/Generic.php:215]
#3  PHPDaemon\Request\Generic->eventCall()
#4  EventBase->dispatch() called at [/opt/phpdaemon/PHPDaemon/Core/EventLoop.php:149]
#5  PHPDaemon\Core\EventLoop->run() called at [/opt/phpdaemon/PHPDaemon/Thread/Worker.php:215]
#6  PHPDaemon\Thread\Worker->run() called at [/opt/phpdaemon/PHPDaemon/Thread/Generic.php:146]
#7  PHPDaemon\Thread\Generic->__invoke() called at [/opt/phpdaemon/bin/phpd:71]

[PHPD] ExampleWebSocket: 'pong' received by client.
[PHPD] ExampleWebSocket: 'pong' received by client.
[PHPD] ExampleWebSocket: 'pong' received by client.
[PHPD] ExampleWebSocket: 'pong' received by client.
[PHPD] ExampleWebSocket: 'pong' received by client.

вторник, 21 июня 2016 г.

Asterisk. Выполнение скрипта при ответе абонента.

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

Реализуется это с помощью конструкции в диалплане (в файле extensions.conf):
[from_12]
exten => 293ХХХХ,1,Dial(SIP/${EXTEN},120,M(answerscript^${CALLERID(num)}^${EXTEN}))
Опция M в команде Dial заставит Asterisk выполнить макрос (в нашем случае answerscript)  и передать макросу ряд аргументов. Аргументы отделяются между собой символом домика (^).

Макрос запуска внешнего скрипта /var/www/html/test.php описывается в отдельном контексте:
[macro-answerscript]
exten => s,1,System(/usr/bin/php -f /var/www/html/test.php ${ARG1} ${ARG2} )

понедельник, 13 июня 2016 г.

Настройка Fax-to-MAIL во Freeswitch 1.6.
Использование Freeswitch для автоматической отправки факсов.

Fax-to-MAIL

1. Установка ImageMagick – пакета, занимающегося конвертированием изображений
# yum install ImageMagick
Теперь конвертировать документ tiff в pdf можно так:
# convert <имя файла на входе>.tiff -resize 100% -monochrome <имя на выходе>.pdf
Эта возможность понадобиться для преобразования принятого факса к формату, удобному для просмотра. (tiff – формат, в котором происходит прием факса в Freeswitch, pdf – нужный нам формат)

2. Установка mutt – удобной программы отправки почтовых сообщений. Отлично работает в командной строке, поддерживает отправку вложений.
# yum install mutt 
Создаем файл конфигурации:
# touch ~/.muttrc
# nano ~/.muttrc
Содержимое файла:
# My email address
set pop_user="admin@mail.ttk"

# My email account password
set pop_pass="xxxxxx"

# Too many email headers make reading a message difficult
ignore *
unignore From: To: Cc: Subject: Date: #Only these are shown in the header

set from="admin@ttk.ru"
set use_from=yes
set envelope_from="yes"
set realname='Network Administrator'
Отправка простого письма с помощью mutt 
# echo "This is the message body" | mutt -s "Тест" admin@yandex.ru
Отправка письма с вложением
# echo "This is the message body" | mutt -s "Тест" -a "/tmp/123.pdf" -- admin@yandex.ru

Для того чтобы Freeswitch использовал наши данные, прописанные в файле .muttrc, для отправки писем, необходимо поместить файл .muttrc в домашнюю папку пользователя freeswitch.
У пользователя freeswitch домашняя папка - /usr/local/freeswitch
# touch /usr/local/freeswitch /.muttrc
# nano /usr/local/freeswitch /.muttrc
Помещаем в файл, созданную ранее конфигурацию
Выставляем права доступа:
# chown freeswitch:freeswitch /usr/local/freeswitch/.muttrc
# chmod 644 /usr/local/freeswitch/.muttrc

3. Создаем скрипт отправки факса по электронной почте
# mkdir /usr/bin/script_freeswitch
# touch /usr/bin/script_freeswitch/sendmailfax.sh
# chmod +x /usr/bin/script_freeswitch/sendmailfax.sh
# chmod 777 /usr/bin/script_freeswitch/sendmailfax.sh
# nano /usr/bin/script_freeswitch/sendmailfax.sh
На вход скрипта получаем перечень e-mail адресов для отправки и файл с полным путем, который необходимо отправить.
Содержимое файла:
#!/bin/bash
# $1  - email адреса через запятую
# $2  - название файла

#получаем базовое имя файла
FNAME=`basename $2 | rev | cut -c 6- | rev`
#получаем путь к файлу
PFNAME=`echo "${2%/*}"`

#Конвертируем файл в PDF
convert $2 -resize 100% -monochrome $PFNAME"/"$FNAME.pdf

#перебираем адреса и отправляем почту
IFS=',' read -ra ARR <<< `echo $1`
for (( i=0; i < `echo ${#ARR[@]}`; i++ ))
do
   echo "Здравствуйте! Принят факс. Пересылаем на адрес ${ARR[$i]}." | \
     mutt -s "Принят факс" -a "$PFNAME"/"$FNAME.pdf" -- ${ARR[$i]}
done

# удаляем исходный файл
rm -f $2 #удаляем исходный файл

Использовать скрипт нужно так:
/usr/bin/script_freeswitch/sendmailfax.sh bob@yandex.ru,post@mail.ru, /tmp/12345.tiff
В этом примере мы файл 12345.tiff будем конвертировать в pdf и отправлять на адреса bob@yandex.ru и post@mail.ru.

4. Определяем папку, в которую будут складываться пришедшие на наш номер факсы. 
Для конкретики будем считать, что номер, на котором работает Fax-to-mail – 3479938103. Имя папки  для временного хранения факсов будет такое же как и номер телефона.
# mkdir /tmp/3479938103
# chmod 777 /tmp/3479938103/

5. Общее конфигурирование Freeswitch
# cd /usr/local/freeswitch/conf/
Вносим общие правки в конфиг факса:
# nano autoload_configs/spandsp.conf.xml
Меняем
        <param name="ident"             value="SpanDSP Fax Ident"/>
        <param name="header"            value="SpanDSP Fax Header"/>
На
        <param name="ident"             value="Fax TTK"/>
        <param name="header"            value="Fax TTK Header"/>
Рестартуемся
# systemctl restart freeswitch

Для конфигов номеров fax-to-mail создадим отдельную папку в конфигурации:
# mkdir dialplan/supportsvttk/fax

В файл, определяющий план набора при входящем вызове помещаем директиву, обязывающую просматривать все файлы из каталог fax:
# nano dialplan/supportsvttk/10_in.xml
<X-PRE-PROCESS cmd="include" data="fax/*.xml"/>

6. Создаем файл описания номера отправки fax на e-mail:
# touch dialplan/supportsvttk/fax/3479938103.xml
# nano dialplan/supportsvttk/fax/3479938103.xml
Содержимое файла:
        <include>
        <extension name="test_3479938103">
        <condition field="destination_number" expression="^(3479938103)$">
             <action application="set" data="fax_ident=Fax 3479938103"/>
             <action application="set" data="fax_header=Fax 3479938103 HEADER"/>
             <action application="answer"/>
             <action application="playback" data="silence_stream://2000"/>
                          <action application="set" data="api_hangup_hook=system /usr/bin/script_freeswitch/sendmailfax.sh bob@yandex.ru,post@mail.ru, /tmp/3479938103/${strftime(%Y-%m-%d-%H-%M-%S)}-${caller_id_number}.tiff"/>                <action application="rxfax" data="/tmp/3479938103/${strftime(%Y-%m-%d-%H-%M-%S)}-${caller_id_number}.tiff"/>
             <action application="hangup"/>
        </condition>
        </extension>
        </include>
При попадании в этот план набора через 2000 мс паузы (в трубке вызываемого абонента слышна тишина), будет стартовать прием факса. 
Факс будет принят с номером содержащим дату приемки факса и номер вызывающего абонента. После того, как вызов завершиться (наступит действие api_hangup_hook), выполниться команда: 
/usr/bin/script_freeswitch/sendmailfax.sh bob@yandex.ru,post@mail.ru, /tmp/3479938103/${strftime(%Y-%m-%d-%H-%M-%S)}-${caller_id_number}.tiff, отправляющая принятый файл на электронные адреса.
Выставляем права доступа на созданные файлы и папки:
# chown -R freeswitch:freeswitch /usr/local/freeswitch/conf/
# chmod -R 770 /usr/local/freeswitch/conf/
Перечитываем конфигурацию Freeswitch
# fs_cli -x "reloadxml"


Автоматическая отправка факса без факсового аппарата

Для отправки факсов создадим несколько контекстов в конфигурации Freeswitch:
<context name="faxout">
   <extension name="send">
       <condition field="destination_number" expression="^(.*):(.*)$" break="on-true">
            <action application="answer"/>
            <action application="tone_detect" data="fax 1100 r +3000 transfer fax XML faxoutnow"/>
            <action application="playback" data="ttk/fax_payload_mono.wav"/>
            <action application="set" data="execute_on_fax_failure=system /usr/bin/script_freeswitch/fax_result.sh $1 $2 bad '\\\${fax_result_text}' "/>
            <action application="set" data="execute_on_fax_success=system /usr/bin/script_freeswitch/fax_result.sh $1 $2 ok '\\\${fax_result_text}' "/>
            <action application="txfax" data="$1"/>
            <action application="hangup"/>
       </condition>
   </extension>
</context>

<context name="faxoutdtmf">
   <extension name="send">
       <condition field="destination_number" expression="^(.*):(.*):(.*)$" break="on-true">
            <action application="tone_detect" data="fax 1100 r +3000 transfer fax XML faxoutnow"/>
            <action application="playback" data="silence_stream://300"/>
            <action application="send_dtmf" data="$3"/>
            <action application="set" data="execute_on_fax_failure=system /usr/bin/script_freeswitch/fax_result.sh $1 $2 bad '\\\${fax_result_text}' "/>
            <action application="set" data="execute_on_fax_success=system /usr/bin/script_freeswitch/fax_result.sh $1 $2 ok '\\\${fax_result_text}' "/>
            <action application="txfax" data="$1"/>
            <action application="hangup"/>
       </condition>
   </extension>
</context>

<context name="faxoutnow">
   <extension name="fax">
       <condition field="destination_number" expression="^fax$" break="on-true">
            <action application="set" data="execute_on_fax_failure=system /usr/bin/script_freeswitch/fax_result.sh $1 $2 bad '\\\${fax_result_text}' "/>
            <action application="set" data="execute_on_fax_success=system /usr/bin/script_freeswitch/fax_result.sh $1 $2 ok '\\\${fax_result_text}' "/>
            <action application="txfax" data="$1"/>
            <action application="hangup"/>
       </condition>
   </extension>
</context>

Контекст faxout используется для дозвона абоненту, проигрыванию ему сообщения типа «Вас приветствует компания такая-то, примите пожалуйста факс» и затем передачи абоненту факса.
Контекст faxoutdtmf будет использоваться для дозвона на номер и набора добавочного номера, а затем отправку факса.
Контекст faxoutnow используется в случае, если при установлении соединения через контексты faxout или faxoutdtmf был детектирован сигнал факса, то система сама перебросит вызов в контекст faxoutnow и отправит факс.
После отправки факса будет запущен скрипт /usr/bin/script_freeswitch/fax_result.sh, который получит на вход переменные, по которым можно определить результат отправки факса и принятии решения о повторной отправке.

Сообщение «Вас приветствует компания такая-то, примите пожалуйста факс» нужно поместить во вновь созданную папку:
# mkdir /usr/local/freeswitch/sounds/en/us/callie/ttk/
помещаем туда файл wav:
/usr/local/freeswitch/sounds/en/us/callie/ttk/fax_payload.wav
Даем права
# chown -R freeswitch:freeswitch /usr/local/freeswitch/sounds/en/us/callie/ttk
# chmod -R 770 /usr/local/freeswitch/sounds/en/us/callie/ttk
Рестартуемся
# systemctl restart freeswitch.service
Теперь к этому файлу можно обратиться в плане набора так:
<action application="playback" data="ttk/fax_payload_mono.wav"/>

Для отправки факса будем использовать команду originate 
В нашем случае команда без донабора добавочного будет выглядеть так:
fs_cli -x "originate {origination_caller_id_name=9735049,origination_caller_id_number=9735049}sofia/gateway/gw-si3000/3031101 /tmp/faxout/test_fax.tiff:121 XML faxout"
Командой устанавливается соединение с номером 3031101 через шлюз gw-si3000. При этом используется АОН 9735049.
Далее вызов передается контексту faxout с параметрами:
/tmp/faxout/test_fax.tiff:121
Первая часть этой строки – это имя файла, вторая часть – это идентификатор факсового сообщения, который я использую для дальнейшей обработки в скриптах, что бы убедиться, что факс дошел. Если факс вдруг не будет передан, то я об этом узнаю.

Команда при использовании донабора будет такая:
fs_cli -x "originate {origination_caller_id_name=9735049,origination_caller_id_number=9735049}sofia/gateway/gw-si3000/3031101 /tmp/faxout/test_fax.tiff:121:123 XML faxoutdtmf"
Здесь так же происходит вызов номера 3031101, но при этом передача вызова производиться в контекст faxoutdtmf. При этом в контекст faxoutdtmf передается строка:
/tmp/faxout/test_fax.tiff:121:123
Первая часть – это файл для отправки
Вторая часть – это идентификатор факса, необходимый в результирующих скриптах
Третья часть – нужный нам добавочный номер.

В результате выполнения команды originate, Freeswitch вернет нам результат принятия задачи и сообщит идентификатор SIP сессии:
Пример:
+OK 02910e93-e8bd-4c45-9671-5db5a8e568eb
Если с вызываемым абонентом соединение не установилось, то команда вернет ошибку и описание
Пример
+ERR User_busy

Для успешной отправки факса и для исключения искажений, необходимо правильно подготовить файл для передачи по факсу.
Команда, преобразующая файл в «правильный» формат такая:
convert -density 204x98 -units PixelsPerInch -resize 1728x1186\! -monochrome -compress Fax <имя_исх_файл> <выходной_файл>.tiff
Исходный файл изображения должен иметь вертикальную ориентацию и иметь соотношение сторон 1186х1728. Команда успешно справляется с конвертированием исходных файлов форматов JPG, JPEG. PDF, TIF, TIFF