WebRTC або як я навчив нашу CRM дзвонити на телефони

Компанія, в якій мені довелося працювати, займається продажем послуг по інтернету. Щоранку чергова зміна розбирає загальний стек накопичилися заявок і починається обдзвін клієнтів для уточнення замовлень. Протягом дня оператори ще й приймають вхідні дзвінки. До початку моєї затії вони використовували для дзвінків такий десктопний SIP-клієнт:
 
 
 
Ця звонилка встановлювалася на комп'ютері кожного співробітника, брала дзвінки і дзвонила, куди треба. Щоб зробити які-небудь зміни в налаштуваннях, потрібно було обійти всі машини і зробити все вручну. При цьому, якщо співробітник працює віддалено, доводилося його консультувати по телефону, як це все зробити. І часто це було досить непросто.
 
Але найголовнішим траблом була відсутність інтеграції з нашою web-системою і базою даних. Такі, здавалося б прості завдання, як відкриття картки клієнта на вхідний дзвінок, збереження статистики дзвінків для кожного зі співробітників і моніторинг їх активності з адміністративного web інтерфейсу — все це дуже складно зробити з десктопним софтфонів, навіть у тому випадку він має відповідні можливості інтеграції з браузером, наприклад за допомогою плагінів.
 
Виникла ідея об'єднати в одній системі і базі даних всю внутрішню роботу і дзвінки. Я довго допілівать нашу CRM з функцією вбудованої звонилки c записом розмов.
Для реалізації дзвінків розглянув ряд технологій і прийшов до висновку, що їх не так вже й багато. Знайшлася пара опенсорсний і комерційних реалізацій, а так само кілька SAAS сервісів, які не підходили в силу внутрішніх політик безпеки — обробляти дзвінки через власний сервер.
 
На початку намагався використовувати sipml5 :
 
 image
 
Документацію довелося збирати по шматках з мережі. В результаті отримав більш-менш робочий телефон з SIP стеком на стороні браузера:
 
 image
 
Установка, тестування і налаштування тривали близько 2 тижнів, в результаті знайшов ряд дрібних, але неприємних багів, які так і не вдалося обійти, наприклад один з них був пов'язаний з настройками Websockets через SSL. А після виходу Chrome браузера 35 версії web телефон відмовився працювати зовсім.
 
Крім цього мені не хотілося розкривати SIP акаунти операторам, а SIP стек на стороні браузера передбачає їх відкрите використання і відправку через Websockets. Навіть якщо Websockets працюють через SSL, у потенційного зловмисника є можливість отдебажіть js код і витягнути SIP пароль. Був варіант — делегувати SIP Digest аутентифікацію нашому Web серверу, але до його реалізації дістатися так і не вдалося.
 
Так виглядають SIP запити на стороні браузера в отладочной консолі:
 
 image
 
Повний доступ до SIP стеку з Javascript мати в загальному не погано. У цьому є свої переваги, наприклад можна спробувати поправити небудь інтеграційний баг в JS SIP Сигналінг. Але тут є один нюанс. Трохи більше ніж 90% SIP вендорів не підтримують зараз специфікацію The WebSocket Protocol as a Transport for the Session Initiation Protocol (SIP) RFC 7118 датируемую січнем 2014 року по якій працює JS SIP, а це означає що webrtc2sip модуль повинен працювати як stateful SIP проксі і фактично дублювати підтримку SIP стека на стороні сервера. Такий розклад здався сильно складним для подальшої роботи та підтримки і я вирішив піти від SIP стека на стороні браузера і знайти яке-небудь більш просте і зрозуміле API для таких завдань з серверної частиною, яку можна було б хостити у себе.
 
У результаті почав тестувати Web Call Server . Це не SAAS і дозволяє обробляти дзвінки через свій сервер, що в даному випадку і було потрібно:
 
 image
 
За функціями приблизно те ж саме що і у sipml5, ті ж WebRTC дзвінки на SIP і назад. Є ще підтримка Flash, але в ній не було необхідності, оскільки всі оператори використовують в основному Chrome і Firefox браузер, а тим, хто використовує IE, довелося пересісти на більш "правильні" браузери.
 
У навантаження дається софтфон на JS з відкритим вихідним кодом , який можна перемалювати і адаптувати для web-сторінки.
 
Основна відмінність від sipml5 — це взаємодія з сервером через API, а не через SIP over Websockets. Тобто SIP стека на стороні браузера немає. Він розташовується тільки на стороні сервера. Це трохи полегшило завдання front-end розробнику, т.к. SIP стек на стороні браузера валив його в сум'яття, а при роботі з Javascript API і CSS стало можливим зосередитися на інтерфейсній частини.
 
 image
 
Отже, як я все це впроваджував.
 
1. Взяв ось такий сервер на Amazon EC2 :
Пам'яті і дискового простору багато не потрібно. Хіба що для логів. А обчислювальні потужності CPU в таких завданнях можуть бути важливі, тому взяв не найслабший інстанси.
 
 image
 
2. Підняв Apache для web-інтерфейсу, встановив і запустив WCS сервер.
 
 image
 
3. На сторінці хрому з'явився стандартний web-телефон, код якого знаходиться на github .
 Інтерфейс телефону мені не дуже сподобався, відразу ж вирішив його редизайн, а отладочная консоль праворуч виявилася цілком корисною. Шкода, що пізніше її довелося прибрати, щоб не лякати нормального користувача.
 
 image
 
4. Протестував web-телефон на здатність дзвонити. Використовував для цього наші колишні SIP-акаунти. Все працює, як треба. І на мобільники дзвонить, і на SIP-телефони, і утримування дзвінків і трансфер, і блекджек і…
 
 image
 
Схожим чином працює созвона з мобільним телефоном.
 
5. Адаптував код web-телефону для своєї web-CRM, перемалював його дизайн і тепер він виглядає так:
 
 image
 
На адаптації варто зупинитися докладніше, тому перемальовуванням дизайну справа не обмежилася.
Першою ж серйозним завданням стала автоматична реєстрація web-телефону на SIP сервері. В іншому випадку, операторові довелося б вводити SIP логін і пароль повторно, вже після того, як він ввів логін і пароль для CRM системи. Постало питання, як інтегрувати.
 
Виявилося, що в API є для цього спеціальна функція loginByToken:
 
 function loginByToken (token) {
 trace («Phone — loginByToken" + token);
 connectingViewBeClosed = false;
 var result = flashphoner.loginByToken (flashphonerLoader.urlServer, token, document.URL);
 closeLoginView ();
 openConnectingView ("Connecting ...», 0);
}

 
Для того щоб розібратися, як ця функція працює, довелося добре постаратися.
C допомогою документації і прикладів вдалося з'ясувати, що все це працює приблизно так:
 
 image
 
1) При створенні токену на стороні CRM використовується алгоритм шифрування AES, яким зашифрована рядок, що включає SIP логін і пароль користувача, а так само іншу необхідну інформацію.
 
Ключ шифрування відомий тільки нашого сервера, де розгорнута CRM, а так само WCS серверу. Крім того, термін дії токену задається спеціальним атрибутом expires для того, щоб не було можливості їм повторно скористатися.
Криптування токену відбувається в AES CTR mode. Нижче приклад c openssl, в якому відбувається генерація шифрованого токену з передачею SIP пароля:
 
 echo-ne '<root status = «ok» description = «test» registerRequired = «true» login = «user5» authenticationName = «user5» password = «password» outboundProxy = «proxy.my» domain = «proxy. my »port =« 5060 »visibleName =« AAA »api_key =« App1 »expires =« 1394839040761100000 »/> ' | Openssl enc-aes-128-ctr-nosalt-K 8263D535FFFFFFFF7B0F60-iv 00000000000000000000000000000000 | xxd-p
 
В результаті отримав щось на кшталт:
 
 CRM:cf4693eedaafda1390b261dcf29d45bd3556d64b1f69cd84db8c3ac8721e7e139b80be75e39da18154e897596e9317084faee0d24d6a6197b62a93a2647b263059167b2664179a5866738260c77372e04fe22104ebe1c7530e9215f50d111fd24384755d28d06673e866159c0b6b83289c045619e8481f9c2a6b56b182f393a7dea06b38b7856436895402a5b40f0525a17822ae0f3204b606e4f0169d1ca9176e8e1b696683d12c7db8208946c204e94f3c8ff285f2bcef4ca9b12187cf541ce37d508d3663ef65f944b01db9aea5c0f10002a376d051cbf1b19bc34f76b6d2a4e1ad1450ae412b51b3af1d3860167f5416b3d2c9eeff94d60b82279e8685beb543893e8a09dee640d7366e478d0d1ee7368e0b63b511
 

 
Зліва назва нашого застосування "CRM", а праворуч созданий раніше токен.
Вставляю цей токен в конфіг flashphoner.xml web-телефону в такому вигляді:
 
 CRM: cf4693eed…
 
У цьому випадку процедура автоматичної реєстрації по токені почнеться відразу ж після перезавантаження сторінки.
2 і 3) loginByToken і розшифровка.
 
На стороні сервера в конфіги прописані ключі шифрування для AES:
 
 CRM = 8263D535FFFFFFFF7B0F60
 
Таким чином, коли приходить токен з префіксом "CRM:" для його розшифровки використовується відповідний ключ.
 
В результаті розшифровки WCS сервер отримує зашифровану раніше рядок:
 
 <root status = «ok» description = «test» registerRequired = «true» login = «user5» authenticationName = «user5» password = «password» outboundProxy = «proxy.my» domain = «proxy.my» port = «5060» visibleName = «AAA» api_key = «App1» expires = «1394839040761100000» />
 
і з цієї XML рядки бере всі дані необхідні для SIP реєстрації.
 
3) Як тільки сервер розшифрував дані, він посилає SIP REGISTER запит на SIP і на 401 відповідь віддає вже нормальну Digest аутентифікацію з використанням розшифрованих на попередньому кроці SIP логіна і пароля.
 
 REGISTER sip: sipnet.ru; lr SIP/2.0
Call-ID: 345ec5157b1a66de3a3a275bdba36197@192.168.1.90
CSeq: 2 REGISTER
From: <sip:crm1@sipnet.ru>; tag = 73a499a8
To: <sip:crm1@sipnet.ru>
Via: SIP/2.0/UDP 192.168.1.90:30000; branch = z9hG4bK2622ce723c34760d6a3f43dd631329e1
Max-Forwards: 70
User-Agent: WebRTC
Allow: UPDATE, MESSAGE, BYE, ACK, REFER, INVITE, NOTIFY, INFO, OPTIONS, CANCEL
Contact: <sip:crm1@192.168.1.90:30000>; expires = 3600
Expires: 3600
Authorization: Digest username=«crm1»,realm=«etc.tario.ru»,nonce=«4A0674BEDF81E0B3F65D»,uri=«sip:sipnet.ru;lr»,response=«0762b862c544007f4fb7c43277312a3d»,algorithm=MD5,opaque=«opaq»,qop=auth,cnonce=«1234567890»,nc=00000001
Content-Length: 0

 
У цьому випадку SIP логін і пароль знають тільки сама CRM і Web Call Server. На браузер ці дані у відкритому вигляді не потрапляють.
Таким чином мені вдалося впровадити телефон в сторінку оператора, не змушуючи його зберігати два різних аккаунта — один для CRM інший для SIP, тому що це дуже незручно. Тепер відразу після завантаження сторінки викликається loginByToken і телефон переходить в стан готовності.
 
Деякі результати впровадження браузерних дзвінків:
 
1. Дзвінки тепер робляться з сайту і приймаються на сайті, де всі дії фіксуються в системі.
 
2. Стало можливим прослуховування записаних розмов, що допомагає вирішувати конфліктні ситуації з клієнтами і розбіжності між співробітниками. Це важливо для нашого розподіленого офісу.
 
3. Кількість прийнятих дзвінків збільшилася приблизно на 20%. Стало зрозуміло, що оператори не завжди піднімали трубку при дзвінку клієнта.
На поточний момент можна сказати, що все працює, як задумано. Проблемні ситуації вдалося вирішити без серйозного занурення в SIP матчастину.
 
З недоліків можна відзначити неможливість установки під Windows. До речі, з установкою під Linux та інтеграцією теж довелося повозитися і, схоже, що подужає її тільки просунутий користувач / розробник.
 
WebRTC аудіо дзвінки працюють стабільно і без будь-яких додаткових браузерних плагінів, типу Flash Player. Так що можна сказати, що мені вдалося реалізувати задуману інтеграцію і два тижні роботи були витрачені не даремно.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.