OpenSource

В предыдущих статьях мы обсудили основные преимущества и возможности Docker. Сегодня я хочу рассказать о реализации сервера Webitel Collaboration с помощью этой технологии. Напомню, что не так давно, с выходом bpm’online 7.4.1, был анонсирован новый продукт для бесплатных внутренних коммуникаций. Вот его мы сегодня и разберем по кирпичиках.

Микросервисы

Помните, микросервисы в основе приложения? Именно эта идеология и заложена в Collaboration. Мы разбили все компоненте на несколько небольших контейнеров в зависимости от выполняемой задачи. А какие основные задачи Collaboration? Если вкратце, то это внутренние звонки. Здесь нет статистики, нет записей разговоров, нет внешней маршрутизации и провайдеров связи.

Архитектура решения

Webitel Collaboration Cloud

Как бы я этого не хотел, но с факсами сталкиваемся. В этой заметке хочу описать как можно организовать принятие факсов в Webitel с дальнейшей отправкой на Email.

Нам понадобится утилита convert.exe из пакета ImageMagick. Утилиту забрасываем в папку CallManager\ (с ее помощью будим конвертировать TIF в PDF для отправки на Email). Так же, создадим папку CallManager\htdocs\storage\FAX — тут будут хранится оригиналы полученных факсов в TIF формате. Не забываем про настройки SMTP клиента, иначе ничего никуда не отправится!

Собственно Lua-скрипт довольно простой:

t38 = "false";
CallerID = session:getVariable("caller_id_number");
FaxDir = session:getVariable("storage_dir").."/FAX/";
FaxFile = FaxDir..os.date("%Y").."_"..os.date("%m").."_"..os.date("%d").."_"..os.date("%H").."-"..os.date("%M").."_"..CallerID..".tif";

session:answer();
session:sleep(1000);
session:execute("playback", "users/auto_fax.wav");
session:execute("playback", "users/press_start_to_receive_a_fax.wav");
session:execute("set", "fax_enable_t38_request="..t38);
session:execute("set", "fax_enable_t38="..t38);
session:execute("playback", "silence_stream://2000");
session:execute("rxfax", FaxFile);

freeswitch.email("to@webitel.ua",
	"from@webitel.ua",
	"subject: FAX from "..CallerID.."\n",
	"Приветствую!\n\nВам отправили FAX, который я прикрепил во вложение.\n\n--\nВаш, Webitel",
	FaxFile,
	"convert.exe",
	"pdf");

session:hangup();

Осталось назначить внутренний номер для этого скрипта и все факсы будут сохранятся на сервере:

Принятые Факсы
Принятые Факсы

А так же, приходят на Email:

Факс на Email
Факс на Email

FreeSWITCHДумаю, что многие используют переадресацию звонков на мобильный, когда вне офиса. Да, это очень удобная функция современной АТС: уходишь, включил переадресацию на мобильный (или по расписанию) и принимаешь важные звонки… Но, одним из неудобных моментов такой переадресации — отсутствие понимания с какого номера пришел звонок (у нас то всегда отображается офисный номер!). Для себя я решил этот вопрос следующим образом: приходит переадресованный звонок, разговариваю, надо узнать номер звонящего — нажимаю *9 и получаю SMS. Как это работает? В предыдущем посте я описал как отправить SMS о пропущенном звонке, так что останавливаться на самом SMS не буду. Все, что нужно, это добавить в dialplan, когда срабатывает переадресация, вызов вот такого action:

<action application="bind_meta_app" data="9 b s lua::smsinfo.lua ${cell}"/>

Я использую application bind_meta_app, который позволяет привязывать в уже отвеченном разговоре на заданную комбинацию DTMF запуск разных сценариев. Вот я и запускаю на *9 Lua скрипт с отправкой SMS:

local CallerName = session:getVariable("caller_id_name")
local CallerNumber = session:getVariable("caller_id_number")

function urlencode (s)
 return (string.gsub (s, "%W",
        function (str)
                return string.format ("%%%02X", string.byte (str) )
        end ))
end

local SMS = '<?xml version="1.0" encoding="UTF-8" ?> \
<request method="send-sms" login="login" passw="password"> \
<msg id="1" phone="' ..cell.. '" sn="IT-SFERA" encoding="cyr">Абонент: ' ..CallerName.. ' \
Номер: ' .. CallerNumber.. '&lt;/msg>&lt;/request>'

if (cell) then
        api = freeswitch.API();
        local response = api:execute("curl", "http://sms.it-sfera.com/websend/ post " ..urlencode(SMS) )
        freeswitch.consoleLog("DEBUG","Here's response:\n" .. response .. "\n")
end

И получаем SMS:

Абонент на линии
Абонент на линии

FreeSWITCHИнтересной функцией для офисной АТС может стать отправка SMS сотруднику о пропущенном звонке. Я опишу, как реализовать этот функционал на FreeSWITCH.

1. Формат SMS
Мой SMS провайдер принимает сообщения через HTTP XML API. Мне необходимо будет сформировать XML файл и передать его методом POST на URL шлюза. Использовать будем модуль mod_curl. Здесь проблем нет. Только нужна будет функция для преобразования XML в urlencode…

2. Когда и куда отправлять
Что бы ответить на вопрос куда отправить, в directory я к регистрации каждого сотрудника добавил переменную cell с мобильным. Теперь из скрипта можно будет получить эту информацию. А когда отправлять? Здесь 2 проверки:
1. причина разрыва коммутации «ORIGINATOR_CANCEL»
2. возможно абонент Вам оставил голосовое сообщение?

3. Как вызвать скрипт?
Из dialplana 🙂 Просто добавьте вот такой action в extension с локальными пользователями:

4. А теперь сам скрипт на Lua

local send = false
local cell = env:getHeader("cell")
local hangup_cause = env:getHeader("hangup_cause")
local bridge_channel = env:getHeader("bridge_channel")
local CallerName = env:getHeader("Caller-Caller-ID-Name")
local CallerNumber = env:getHeader("Caller-Caller-ID-Number")

function urlencode (s)
 return (string.gsub (s, "%W",
        function (str)
                return string.format ("%%%02X", string.byte (str) )
        end ))
end

local SMS = ' \
 \
'

if (hangup_cause == "ORIGINATOR_CANCEL" and cell) then
        SMS = SMS.. "Вам звонили: " ..CallerName.. " " .. CallerNumber
        send = true
end

if (bridge_channel == "loopback/voicemail-a" and cell) then
        SMS = SMS.. "Новое сообщение от " ..CallerName.. " " .. CallerNumber
        send = true
end

if (send) then
        SMS = SMS.. ""
        api = freeswitch.API();
        local response = api:execute("curl", "http://sms.it-sfera.com/websend/ post " ..urlencode(SMS) )
        freeswitch.consoleLog("DEBUG","Here's response:\n" .. response .. "\n")
end

5. О, sms-ка пришла!

У Вас пропущенный звонок!
У Вас пропущенный звонок!

Сегодня поговорим о безопасных разговорах 🙂 Вы конечно же поняли: шифрование в SIP. Хочу научить FreeSWITCH использовать TLS (вместо UDP) и шифровать голос по SRTP (с помощью ZRTP), в качестве клиента будет телефон Yealink.

Я искал модуль, который бы мог воспроизводить и записывать MP3 в FreeSWITCH и нашел. Но, как оказалось Mod_shout не только это умеет, а еще и воспроизводить потоковое вещание (Интернет-радио), которое можно подключить вместо Music On Hold. Вот простой пример диалплана:

<extension name="RadioRocks">
     <condition field="destination_number" expression="^0000$">
       <action application="answer"/>
       <action application="playback" data="shout://online-radioroks.tavrmedia.ua:7000/RadioROKS"/>
       <action application="hangup"/>
     </condition>
   </extension>

Послушать радио можно через эту кнопку »

Звонок с сайта«.

FreeSWITCHЯ уже описывал, как в Oktell организовать сценарий Callback для сотрудников (звонок за счет офисной АТС). Теперь рассмотрим как такое же реализовать с помощью FreeSWITCH.

Для этой функции я выделил отдельный городской SIP-номер (благо, у меня их 10 шт.). Пускай Вас не смущает, что придется звонить с мобильного на городской, мы сделаем все возможное отправим в trunk SIP 183 Session Progress Message, что бы сотрудник не потерял и копейки. Вот таким будет наш диалплан:

    <extension name="380322320000">
    <condition field="destination_number" expression="^(380322320000)$">
            <action application="pre_answer"/>
            <action application="read" data="3 12 'tone_stream://%(10000,0,350,440)' digits 20000 #"/>
            <action application="set" data="api_hangup_hook=jsrun callback.js ${caller_id_number} ${digits}"/>
            <action application="hangup" data="CALL_REJECTED"/>
    </condition>
    </extension>

Поступил звонок, отправили Session Progress и гудок. Ждем ввода номера, после чего завершаем соединение. А вот уже после завершения запускаем JavaScript callback.js, где на вход отправляем номер звонящего и набранные цифры. Вот код скрипта callback.js:

    if ( ['0636150000','0939230000'].indexOf(argv[0]) != '-1' ) {
    console_log("info","Callback to " + argv[0] + "\n");
    session = new Session("{ignore_early_media=true}sofia/gateway/gsm/" + argv[0]);
    while (session.ready()) {
            session.execute("execute_extension", argv[1]+ " XML default");
            }
    }

Здесь все просто: проверяем номер ли нашего сотрудника, дозваниваемся ему и соединяем с набранным ранее абонентом.

FreeSWITCHЯ описывал, как можно отправить SMS-визитку после звонка в Oktell. Пришла очередь сделать аналогический функционал на FreeSWITCH! Для этого я создаю в MySQL базе на сервере отдельную табличку, где буду хранить информацию о том, кому уже отправил визитку. В качестве языка написания сценария я выбрал JavaScript. Сценарий проверяет в табличке была ли отправлена уже SMS-визитка (на 1 номер должна только 1 уходить SMS), если нет, тогда смотрим код оператора (я смотрю украинских операторов) и для каждого из операторов создаем SMS. Отправляем через нашего SMS-провайдера.

А вот и сам JavaScript: