![image](https://habrastorage.org/getpro/habr/post_images/89a/ce7/961/89ace7961a9336331ffe5c7aa0e0cd42.jpg)
Заставить Miranda осуществлять звонки — несложно, используя плагины для SIP-телефонии, превращающие Miranda в софтфон,
Однако, что же делать когда у пользователей нет ни гарнитур, ни микрофонов?
А уж обучить пользователей работать с софт-фоном, встроенным с Miranda — страшный сон.
Вариант один — использовать существующие телефонные аппараты (IP, аналог, цифра) для осуществления звонков из Miranda.
Исходные данные:
1. Цифровая мини АТС Panasonic TDE200, к которой подключены все телефоны
2. Куча абонентов с разношерстными типами телефонов: IP, аналог, цифра, системные телефоны
3. Рабочий XMPP-сервер на базе OpenFire
4. Клиент Miranda для общения по протоколу XMPP (Jabber) — установлен у всех пользователей.
Хотелки:
1. Возможность совершать звонок, используя клиент Miranda.
2. Звонок и разговор должен совершаться по обычным рабочим телефонам (IP, аналог, цифра).
3. Информация о номерах телефонов должна браться из Active Directory.
4. Прозрачность/простота использования для пользователей.
5. При совершении звонка добавить оповещение по Jabber о звонящем.
Кого заинтересовало вступление — добро пожаловать под кат. Ниже будет интересно.
Предыстория
Несколько лет назад, в поисках решения для корпоративного IM-сервиса в Интернете наткнулся на статью ИТшников из города Киров, про корпоративный IM-Сервис на OpenFire с прозрачной аутентификацией пользоваталей, где в роли клиента выступала Miranda, да не простая, а с возможностью инициализации удаленного подключения по RDP и VNC к компьютеру, того пользователя которого вы выбрали в контакт-листе, при этом Вам нет необходимости вводить DNS-имя компьютера пользователя, оно автоматически подставляется в адресное поле, и устанавливает соединение.
Я довольно долго пользовался их сборкой Miranda, но со временем стали появляться глюки в работе, нестабильность подключения, вылеты клиента.
В итоге было принято решение — собрать свою Miranda
Новый клиент
Собрать свою сборку Miranda — дело не хитрое: установил последнюю версию ядра накопировал нужных плагинов и вуаля — своя сборка.
Но статья не про это.
Новый клиент Miranda полностью дублировал функционал подключения к RDP и VNC, однако стал намного стабильнее ввиду обновленного ядра (буду называть его — «админский клиент»). К слову сказать — для пользователей была собрана своя Miranda, но уже без «плюшек» подключения (пользовательский клиент).
В течении нескольких месяцев клиент успешно работал у нашей группы технической поддержки — нареканий не было.
За это время я успешно реализовал мониторинг за Active Diretory (статья тут и тут), за файловым сервером (тут) и VPN-серверами.
В процессе создания мониторинга, несколько раз слышал от ребят из технической поддержки, что было бы не плохо еще добавить в клиент возможность подключения к пользователю, используя «Удаленный помощник», т.к. у нас он является стандартом дэ-факто для поддержки пользователей
Собственно, это не доставило каких-то трудностей.
Казалось бы — что еще нужно? Все необходимое для поддержки присутствует.
Небольшое отступление
В тот момент я увлекся программированием на C# — переписал весь мониторинг с Powershell на C#, прикрутил мониторинг к базе данных (раньше писал в текстовые файлы) и наваял простенькую web-страничку с php, которая подтягивала данные о мониторинге из базы данных — стало совсем хорошо.
На страничке можно было сделать выборку:
1. для изменений в AD: по имени пользователя или имени компьютера
2. для файлового сервера: по имени пользователя или имени файла
3. для VPN-серверов: по имени пользователя
Обновленный новый клиент
![image](https://habrastorage.org/getpro/habr/post_images/476/d72/a2a/476d72a2ae42d5aba511d47ae32f9959.jpg)
Для еще большего удобства технической поддержки пользователей было решено добавить выборку по необходимому пользователю в клиент Miranda.
Как это работает:
Для подключения к примеру по RDP, Miranda при помощи определенного плагина может возвращать не только значение имени компьютера пользователя, но и логин данного пользователя.
В клиент были добавлены функции открытия web-страниц с таблицами мониторинга, в URL которых передавались GET'ом логин необходимого пользователя.
В итоге клиент приобрел совсем другую окраску — теперь он не только позволяет осуществлять поддержку в режиме реального времени, но и производить анализ по прошедшим событиям, касающегося данного пользователя (к примеру таким образом можно выяснить какие файлы данный пользователь открывал или удалял на сетевом ресурсе, к каким серверам он пытался подключаться, какие действие производились с его учетной записью в AD).
Далее мной было написано простенькое WindowsForm приложение на C#, которое, используя WMI-провайдера, может получать список процессов удаленного компьютера, с возможностью их завершения.
Не долго думая, данное приложение было «внедрено» в функционал админского клиента.
Теперь поддержка пользователей получила новую окраску: нет необходимости вести таблицы с данными о компьютерах, IP-адресах, пользоваталей, которые работают на данных компьютерах, и уж тем более теперь нет необходимости запрашивать информацию об имени или IP-адресе компьютера у пользователя (при условии конечно, что сеть все же у него есть, иначе Miranda не сможет «взять» имя компьютера пользователя, находящегося в оффлайне, но логин пользователя — сможет).
То была предыстория, теперь основная тема — возможность осуществлять звонки
Как говорится: Лень — двигатель прогресса.
Данный клиент до минимума свел ввод данных вручную для подключения к пользователю или поиску информации о нем в базе данных мониторинга — все делается 2мя кликами.
В процессе использования Miranda с расширенным функционалом в течении 2 лет, меня периодически посещала мысль о том, что было бы неплохо сделать возможность осуществлять звонки используя клиент Miranda — без необходимости набора номера вручную.
Одна проблема — у нас нет IP-телефонии, точнее она есть, но используется у 5% абонентов:)
Т.е. хочется сделать так, чтобы при выборе пользователя в контакт-листе Miranda, можно было выбрать действие — «звонить», а разговаривать уже по своему привычному рабочему телефону.
И вот однажды сам себе поставил задачу — реализовать это. Сказано — сделано!
Дано:
1. АТС Panasonic TDE200
2. 300 абонентов (15 — SIP, 285 — аналоговые или цифровые телефоны)
3. Miranda (куда же без нее)
4. Сверлящая идея, не дающая спать ночами:)
Пойдем от обратного, т.е. от телефонов до Miranda.
Связываем Panasonic с LAN
Первое, о чем я задумался, как Miranda достучится до АТС.
Решение оказалось незамысловатое — Asterisk.
Процедуру установки и конфигурирования Asterisk'а я затрагивать не буду, в сети и без того много статей на эту тему.
Объединяем Asterisk и Panasonic TDE200 SIP-транком, по данной статье.
Сделано. Теперь используя компьютер, можно совершать звонки на телефоны пользователей. Однако нам нужно совершать звонки с обычных телефонов, без набора номера. Копаем дальше.
CallBack
Asterisk — мощная программная IP-АТС, все что можно придумать с телефонией — Asterisk умеет делать.
В том числе у него есть замечательная функция как CallBack, т.е. обратный вызов — от этого и будем плясать.
Так же Asterisk умеет формировать звонок автоматически, используя специально сформированные файлы звонков (call-файлы). В них присутствует информация о номерах телефонов, на которые необходимо сделать вызов. Данные файлы при помещении в специальную папку на Asterisk'е и инициируют вызов обоих абонентов и соединяет их.
Пример текста файла:
Channel: SIP/utde/9311264
CallerID: test <9311264>
Context: from-internal
Extension: 994120031
Это значит, что астериск должен позвонить по номерам: 9311264 (наш номер) и 994120031 (куда звоним). Звонок по обоим номерам уходит с номера 9311264 (т.е. с нашего, указан в свойстве CallerID).
Автоматизируем создание Call-файлов
Для автоматизации воспользуемся обычным bash-скриптом, который при помощи команды echo будет писать в новый call-файл необходимые данные для совершения звонка: т.е. номера телефонов того, кто звонит и того, кому звонят. Оба номера телефона будем передавать параметрами при запуске bash-скрипта, например: call.sh 945954 945932
Пример скрипта, для формирования Call-файла:
#!/bin/bash
#
outcall="/var/spool/asterisk/$1-$2.call"
echo Channel: SIP/utde/$1 > $outcall
echo CallerID: 'call <'$1'>' >> $outcall
echo Context: from-internal >> $outcall
echo Extension: $2: >> $outcall
chown asterisk:asterisk $outcall
mv $outcall /var/spool/asterisk/outgoing
Как запустить bash-скрипт «извне»
Все это замечательно, мы уже можем с помощью запуска скрипта и передачей ему 2х параметров, заставить совершить звонок на телефоны двух сотрудников и объединить их в разговор.
Но каким образом нам заставить это делать, не находясь в консоли на сервере, где находится Asterisk?
Ответ довольно прост — web.
Развернем apache, сформируем php-страницу, которая будет запускать bash-скрипт и передавать ему 2 номера телефона в ввиде параметров.
Запустить bash_скрипт из php возможно следующей функцией:
shell_exec("sudo /var/scripts/out.sh $from $to");
Где имя скрипта: out.sh
Ваш номер телефона в параметре: $from
Номер, куда звоним: $to
Передаем данные в php
Так же как и bash-скрипт, php-страница должна получить 2 номера телефона. Самым простым решением данной проблемы является метод GET.
Т.е. специально сформированная URL, несущая в себе 2 номера телефона, передает их в php, где они уже передаются в bash.
Передать с помощью метода GET в переменную не сложно, достаточно в php-коде указать:
$from=$_GET['from'];
$to=$_GET['to'];
Т.е. переменным $from и $to присваиваем значение которые были переданы в url в параметрах from и to,
Например ссылка: pbx.domain.ru/index.php?from=943216&to=987632
передает в переменную $from номер телефона 943216, а в переменную $to — 987632
Формируем GET-запрос из Miranda
Для решения данной проблемы я не нашел готового решения, поэтому было решено написать собственно приложение, которое бы само формировала нужный URL и делала запрос по нему, передавая номера телефонов. И конечно же реализовал это на C#. Назовем это приложение условно: Приложение C#.
Что делает приложение:
1. Принимает от Miranda входящим параметром — логин сотрудника, которому хотим совершить звонок.
2. Забирает логин текущего пользователя на нашем компьютере (т.е. наш логин)
3. Делает запрос в Active Directory, в котором ищет этих пользователей по их логинам и возвращает их номера телефонов (Телефоны должны быть указаны в атрибуте учетной записи telephoneNumber).
4. Формирует URL в который вставляет 4 значения: номер «кто звонит», номер «кому звонят», логин «кто звонит», логин «кому звонят» (К примеру: pbx.domain.ru/index.php?from=924374&to=859345&fromLogin=ivanov&toLogon=petrov (Логины передаю для создания оповещения, об этом ниже).
5. Делает запрос по данному URL.
На этом «звонилка» готова.
Вот так выглядит теперь контекстное меню админской Miranda:
![image](https://habrastorage.org/getpro/habr/post_images/8dd/597/d7a/8dd597d7a4d290abeff4a3ce6dcc15d6.jpg)
А так у юзеров:
![image](https://habrastorage.org/getpro/habr/post_images/767/99e/fd4/76799efd4c775d7adcedaa338cd82411.jpg)
При выборе функции «Звонок» запускается Приложение C#:
![image](https://habrastorage.org/getpro/habr/post_images/e52/bf1/9b8/e52bf19b8ec467894645b60a8bf3f072.jpg)
Пользователь выбирает на какой номер хочет совершить звонок, и нажимает соответствующую кнопку звонка.
Если мобильный номер не указан в Active Directory, то выводиться он не будет (как и кнопка звонка).
Если номеров у данного пользователя нет, то приложение об этом сообщит.
Создаем оповещение о входящем звонке
Т.к. у большинства пользователей телефоны не имеют контакт листа, то и определить кто звонит достаточно сложно, даже если Вы видите, какой номер Вам звонит, то была добавлена функция оповещения в Jabber о том, кто звонит: ФИО, должность, отдел, номер телефона.
Выше я написал, что приложение передает помимо 2х телефонов еще и два логина.
Т.е. php может принять 4 параметра, не только номера телефонов но и логины.
Дописываем php. Добавляем функцию запроса в Active Directory.
Что в итоге делает php:
1. получив логины, делает запрос в AD на получение ФИО, Должности, Отдела человека, который совершает вызов
$srv = "10.10.10.1"; //ip контроллера домена
$srv_domain = "domain.ru"; //имя домена
$srv_login = "username@".$srv_domain; //имя учетной записи под которой будем логиниться к LDAP
$srv_password = "PASSW0RD"; //пароль от этой учетной записи
$dn = "dc=domain,dc=ru"; //DN-Имя домена
$filter = "(&(sAMAccountName=$fromLogin)(objectCategory=user)(memberOf=CN=Openfire,CN=Users,DC=domain,DC=ru)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))";
$attr = array("cn","telephoneNumber","title","department","company");
$dc = ldap_connect($srv);
ldap_set_option($dc, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($dc, LDAP_OPT_REFERRALS, 0);
if ($dc) {
ldap_bind($dc,$srv_login,$srv_password);
$result = ldap_search($dc,$dn,$filter,$attr);
$result_entries = ldap_get_entries($dc,$result);
ldap_unbind($dc);
}
$DisplayName = $result_entries[0]['cn'][0]; //Получаем ФИО пользователя
$Title = $result_entries[0]['title'][0]; //Должность
$Department = $result_entries[0]['department'][0]; //Отдел
$DisplayName = iconv('utf-8', 'cp1251', $DisplayName);
$Title = iconv('utf-8', 'cp1251', $Title);
$Department = iconv('utf-8', 'cp1251', $Department);
2. формирует сообщение, содержащее информацию о звонящем.
3. Запускает bash-скрипт, в который передает тело сообщения и логин «кому звонят».
#!/bin/bash
#
echo "$2" | sendxmpp $1@domain.ru
bash-скрипт запускает sendxmpp (Perl-скрипт, для отправки сообщений по протоколу XMPP/Jabber, свободно доступен в репозиториях) и передает ему тело сообщения и логин «кому звонят». Sendxmpp отправляет сообщение пользователю от специально-созданного контакта (назовем его Оператор).
Так же в теле сообщение передается ссылка, при открытии которой, создается обратный звонок, человеку, который Вам звонил — сделано для удобства. К примеру пропустили Вы звонок, увидели сообщение, хоть в сообщении и присутствует информация о номере звонящего, но «Лень — двигатель прогресса» и мы не хотим набирать вручную номер на телефоне, или искать данного сотрудника в списке контактов Miranda, чтобы совершить вызов — Мы просто кликаем по ссылке, и вся эта котовасия со скиптами начинает работать:
идут запросы в AD из php
формируются сообщение тому человеку, что вы ему звоните, так же со ссылкой на перезвон
формируется call-файл
звонят телефоны
Так выглядит сообщение:
![image](https://habrastorage.org/getpro/habr/post_images/168/bb4/c16/168bb4c16b6c6cd8da48c4e80d813e3a.jpg)
Заключение
В итоге мы получили полноценный клиент, из которого можно осуществлять подключение к пользователям, просмотр информации по мониторингу, совершать звонки одним кликом.
Плюс данного решения заключается в том, что все данные берутся из Active Directory, что упрощает хранение данных о номерах телефонах — нужно хранить только в одном месте.
Приложение C#, запрашивает данные в Active Directory не только о рабочем номере телефона, но и о мобильном. И мы можем выбрать на какой номер хотим звонить: рабочий или мобильный.
Звонки так же совершаются и на междугородние номера, это особенно актуально для нас, т.к. у нас несколько филиалов в разных городах — что несомненно упрощает жизнь, т.к. не нужно набирать длинные номера телефонов с кодами городов.
Общая схема работы:
![image](https://habrastorage.org/getpro/habr/post_images/4f3/e55/6c8/4f3e556c8d010f0d7559bd26a4f70963.jpg)
Выглядит конечно громоздко, но на практике, после нажатия кнопки «звонок», Ваш телефон начинает звонить через 1-2 секунды.
Безопасность
Все это замечательно работает, но есть нюансы в безопасности:
1. Я кодирую номера телефонов и логинов в приложении на C#, чтобы они не передавались в открытом виде (т.к. ссылка присутствует в тексте оповещения). Php уже их декодирует и получает нормальные данные(номера и логины).
2. Обязательно прочтите в сети о защите Asterisk. Как минимум настройте iptables и разрешите только вашей подсети ходить на Asterisk.
Вопрос: Подскажите, как можно реализовать тайм аут, к примеру, чтобы пользователь не мог совершить звонок через Miranda, в течении 30 секунд, после начала предыдущего звонка? На ум приходит только записывать IP адреса и время откуда пришел запрос на звонок. МБ есть еще какие-то варианты?
PS: Т.к. заносить номера телефонов в Active Directory достаточно утомительное занятие, я написал небольшое WindowsForm приложение на C#, которое единожды запускается на компьютерах пользователей, и просит их указать свои номера телефонов (рабочий и мобильный). Эти данные записываются в соответствующий атрибут учетной записи пользователя в Active Directory. Приложение это нельзя закрыть корме как введя свои данные. Закрыть можно через диспетчер задач, но пользователи обычно до этого не доходят.
Данное приложение было так же добавлено в клиент Miranda, чтобы пользователи всегда могли указать свои номера телефонов, если они изменились. (Тут уже, конечно, приложение можно закрыть без обязательной записи своего номера).
PSS: Спасибо за помощь с Астериском Lanched