Integracja Asterisk z Bitrix24

Integracja Asterisk z Bitrix24
W sieci istnieje wiele opcji integracji IP-ATC Asterisk z CRM Bitrix24, ale postanowiliśmy stworzyć nasze własne rozwiązanie.

Pod względem funkcjonalności wszystko jest standardowe:

  • Klikając w link z numerem telefonu klienta w Bitrix24, Asterisk łączy wewnętrzny numer użytkownika, od którego wykonano kliknięcie, z numerem telefonu klienta. W Bitrix24 rejestrowany jest zapis połączenia, a po zakończeniu rozmowy dołączany jest zapis konwersacji.
  • Na Asterisk przychodzi połączenie zewnętrzne — w interfejsie Bitrix24 wyświetlamy kartę klienta temu pracownikowi, na którego numer to połączenie przyszło.
    Jeśli takiego klienta nie ma, otworzymy kartę tworzenia nowego leadu.
    Gdy tylko połączenie zostanie zakończone, odzwierciedlamy to w karcie i dodajemy zapis rozmowy.

Poniżej opiszę, jak wszystko skonfigurować i podam link do github — tak, tak, pobierajcie i korzystajcie!

Ogólny opis

Swoją integrację nazwaliśmy CallMe. CallMe to mała aplikacja internetowa napisana w PHP.

Wykorzystywane technologie i usługi

  • PHP 5.6
  • Biblioteka PHP AMI
  • Composer
  • Nginx + php-fpm
  • Supervisor
  • AMI (Asterisk Manager Interface)
  • Webhooki Bitrix (uproszczona realizacja REST API)

Wstępna konfiguracja

Na serwerze z Asterisk należy zainstalować serwer www (u nas nginx + php-fpm), supervisor i git.

Komenda do instalacji (CentOS):

yum install nginx php-fpm supervisor git

Przechodzimy do katalogu dostępnego dla serwera www, pobieramy z gita aplikację i ustawiamy odpowiednie prawa do folderu:


cd /var/www
git clone https://github.com/ViStepRU/callme.git
chown nginx. -R callme/

Następnie skonfigurujemy nginx, nasza konfiguracja znajduje się w

/etc/nginx/conf.d/pbx.vistep.ru.conf

serwer {
	server_name www.pbx.vistep.pl pbx.vistep.pl;
	listen *:80;
	rewrite ^  https://pbx.vistep.pl$request_uri? permanent;
}

serwer {
#        listen *:80;
#	server_name pbx.vistep.pl;


	access_log /var/log/nginx/pbx.vistep.pl.access.log main;
        error_log /var/log/nginx/pbx.vistep.pl.error.log;

    listen 443 ssl http2;
    server_name pbx.vistep.pl;
    resolver 8.8.8.8;
    ssl_stapling on;
    ssl on;
    ssl_certificate /etc/letsencrypt/live/pbx.vistep.pl/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/pbx.vistep.pl/privkey.pem;
    ssl_dhparam /etc/nginx/certs/dhparam.pem;
    ssl_session_timeout 24h;
    ssl_session_cache shared:SSL:2m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers kEECDH+AES128:kEECDH:kEDH:-3DES:kRSA+AES128:kEDH+3DES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv2;
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security "max-age=31536000;";
    add_header Content-Security-Policy-Report-Only "default-src https:; script-src https: 'unsafe-eval' 'unsafe-inline'; style-src https: 'unsafe-inline'; img-src https: data:; font-src https: data:; report-uri /csp-report";
	
	root /var/www/callme;
	index  index.php;
        location ~ /.
        {
                deny all; # zakaz dla ukrytych plików
        }

        location ~* /(?:uploads|files)/.*.php$
        {
                deny all; # zakaz dla przesłanych skryptów
        }

        location ~* ^.+.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$
        {
                access_log off;
                log_not_found off;
                expires max; # cachowanie statycznych danych
        }

	location ~ .php {
		root /var/www/callme;
		index  index.php;
		fastcgi_pass unix:/run/php/php5.6-fpm.sock;
	#	fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
		include /etc/nginx/fastcgi_params;
		}
}

Analiza konfiguracji, pytania dotyczące bezpieczeństwa, uzyskanie certyfikatu i wybór serwera www zostawiam na boku – o tym już wiele napisano. Aplikacja nie ma żadnych ograniczeń, działa zarówno przez http, jak i przez https.

Mamy – https, certyfikat Let’s Encrypt.

Jeśli wszystko zrobiłeś poprawnie, klikając w link powinieneś zobaczyć coś podobnego.

Integracja Asterisk z Bitrix24

Konfiguracja Bitrix24

Utworzymy dwa webhooki.

Webhook przychodzący.

Pod adresem konta administratora (z id 1) przechodzimy ścieżkę: Aplikacje -> Webhooki -> Dodaj webhook -> Webhook przychodzący.

Integracja Asterisk z Bitrix24

Wypełniamy parametry webhooka przychodzącego, jak na zrzutach ekranu:

Integracja Asterisk z Bitrix24

Integracja Asterisk z Bitrix24

I klikamy zapisz.

Po zapisaniu Bitrix24 udostępni URL webhooka przychodzącego, na przykład:

Integracja Asterisk z Bitrix24

Zapisz swój wariant URL bez końcowego /profile/ — będzie on używany w aplikacji do obsługi przychodzących połączeń.

Mój to https://b24-xsynia.bitrix24.ru/rest/1/7eh61lh8pahw0fwt/

Webhook wychodzący.

Aplikacje -> Webhooki -> Dodaj webhook -> Webhook wychodzący.

Szczegóły znów na zrzutach ekranu:

Integracja Asterisk z Bitrix24

Integracja Asterisk z Bitrix24

Zapisujemy i otrzymujemy kod autoryzacji.

Integracja Asterisk z Bitrix24

Mój to xcrp2ylhzzd2v43cmfjqmkvrgrcbkni6. Trzeba go również skopiować, jest potrzebny do wykonania połączeń wychodzących.

WAŻNE!

Na serwerze Bitrix24 musi być skonfigurowany certyfikat ssl (można użyć letsencrypt), w przeciwnym razie API Bitrixa nie będzie działać. Jeśli masz wersję chmurową, nie musisz się martwić — tam już jest ssl.

WAŻNE!

W polu „Adres obsługi” musi być podany adres dostępny z sieci Internet!

I ostatnim krokiem zainstalujemy nasze CallMeOut jako aplikację do wykonywania połączeń (aby po kliknięciu na numer na PBX wysyłana była komenda do inicjacji połączenia).

W menu wybieramy: Więcej -> Telefonia -> Więcej -> Ustawienia, ustawiamy w „Numer dla wychodzącego połączenia domyślnie” Aplikacja: CallMeOut i klikamy „Zapisz”

Integracja Asterisk z Bitrix24

Konfiguracja asterisk

Aby zapewnić prawidłowe połączenie między Asterisk a Bitrix24, musimy dodać użytkownika AMI callme do manager.conf:

[callme]
secret = JD3clEB8_f23r-3ry84gJ
deny = 0.0.0.0/0.0.0.0
permit = 127.0.0.1/255.255.255.0
permit= 10.100.111.249/255.255.255.255
permit = 192.168.254.0/255.255.255.0
read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan
write = system,call,agent,log,verbose,user,config,command,reporting,originate

Następnie jest kilka sztuczek, które należy wdrożyć za pośrednictwem dialplanu (u nas to extensions.ael).

Podaję cały plik, a potem dam wyjaśnienia:

globals {
    WAV=\/var\/www\/pbx.vistep.pl\/callme\/records\/wav; \/\/Tymczasowy katalog z WAV
    MP3=\/var\/www\/pbx.vistep.pl\/callme\/records\/mp3; \/\/Gdzie eksportować pliki mp3
    URLRECORDS=https:\/\/pbx.vistep.pl\/callme\/records\/mp3;
    RECORDING=1; \/\/ Nagrywanie, 1 - włączone.
};

macro recording(calling,called) {
        if ("${RECORDING}" = "1"){
              Set(fname=${UNIQUEID}-${STRFTIME(${EPOCH},,%Y-%m-%d-%H_%M)}-${calling}-${called});
	      Set(datedir=${STRFTIME(${EPOCH},,%Y\/%m\/%d)});
	      System(mkdir -p ${MP3}\/${datedir});
	      System(mkdir -p ${WAV}\/${datedir});
              Set(monopt=nice -n 19 \/usr\/bin\/lame -b 32  --silent "${WAV}\/${datedir}\/${fname}.wav"  "${MP3}\/${datedir}\/${fname}.mp3" && rm -f "${WAV}\/${fname}.wav" && chmod o+r "${MP3}\/${datedir}\/${fname}.mp3");
	      Set(FullFname=${URLRECORDS}\/${datedir}\/${fname}.mp3);
              Set(CDR(filename)=${fname}.mp3);
	      Set(CDR(recordingfile)=${fname}.wav);
              Set(CDR(realdst)=${called});
              MixMonitor(${WAV}\/${datedir}\/${fname}.wav,b,${monopt});

       };
};


context incoming {
888999 => {
	&recording(${CALLERID(number)},${EXTEN});
        Answer();
        ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp()); \/\/ ustawiamy CallerID, jeśli rozpoznaliśmy go z Bitrix24
        Set(CallStart=${STRFTIME(epoch,,%s)});  
        Queue(Q1,tT);
        Set(CallMeDISPOSITION=${CDR(disposition)}); 
        Hangup();
        }

h => {
    Set(CDR_PROP(disable)=true); 
    Set(CallStop=${STRFTIME(epoch,,%s)}); 
    Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)}); 
    ExecIF(${ISNULL(${CallMeDISPOSITION})}?Set(CallMeDISPOSITION=${CDR(disposition)}):NoOP(=== CallMeDISPOSITION już został ustawiony ===));  
}

}


context default {

_X. => {
        Hangup();
        }
};


context dial_out {

_[1237]XX => {
	&recording(${CALLERID(number)},${EXTEN});
        Set(__CallIntNum=${CALLERID(num)})
	Set(CallStart=${STRFTIME(epoch,,%s)});
        Dial(SIP\/${EXTEN},,tTr);
        Hangup();
        }

_11XXX => {
	&recording(${CALLERID(number)},${EXTEN});
	Set(CallStart=${STRFTIME(epoch,,%s)});
	Set(__CallIntNum=${CALLERID(num)});
        Dial(SIP\/${EXTEN:2}@toOurAster,,t);
        Hangup();
        }

_. => {
	&recording(${CALLERID(number)},${EXTEN});
        Set(__CallIntNum=${CALLERID(num})
	Set(CallStart=${STRFTIME(epoch,,%s)});
	Dial(SIP\/${EXTEN}@toOurAster,,t);
	Hangup();
        }

h => {
        Set(CDR_PROP(disable)=true);
        Set(CallStop=${STRFTIME(epoch,,%s)});
        Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)});
	if(${ISNULL(${CallMeDISPOSITION})}) {
          Set(CallMeDISPOSITION=${CDR(disposition)});
        }
	System(curl -s http:\/\/pbx.vistep.pl\/CallMeOut.php --data action=sendcall2b24 --data call_id=${CallMeCALL_ID} --data-urlencode FullFname=${FullFname} --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition=${CallMeDISPOSITION});
}

};

Zacznijmy od początku: dyrektywa globals.

Zmienna URLRECORDS przechowuje URL do nagrań rozmów, z którego Bitrix24 będzie je pobierać do karty kontaktu.

Teraz interesuje nas makro makro recording.

Tutaj, oprócz nagrywania rozmów, ustawimy zmienną FullFname.

Set(FullFname=${URLRECORDS}\/${datedir}\/${fname}.mp3);

Przechowuje pełny URL do konkretnego pliku (makro jest wywoływane wszędzie).

Przeanalizujmy połączenie wychodzące:

_. => {
	&recording(${CALLERID(number)},${EXTEN});
        Set(__CallIntNum=${CALLERID(num)})
	Set(CallStart=${STRFTIME(epoch,,%s)});
	Dial(SIP/${EXTEN}@toOurAster,,t);
	Hangup();
        }

h => {
        Set(CDR_PROP(disable)=true);
        Set(CallStop=${STRFTIME(epoch,,%s)});
        Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)});
	if(${ISNULL(${CallMeDISPOSITION})}) {
          Set(CallMeDISPOSITION=${CDR(disposition)});
        }
	System(curl -s http://pbx.vistep.ru/CallMeOut.php --data action=sendcall2b24 --data call_id=${CallMeCALL_ID} --data-urlencode FullFname=${FullFname} --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition=${CallMeDISPOSITION});
}

Załóżmy, że dzwonimy na 89991234567, w pierwszej kolejności trafiamy tutaj:

&recording(${CALLERID(number)},${EXTEN});

czyli wywoływany jest makro do nagrywania rozmowy i ustawiane są odpowiednie zmienne.

Dalej

        Set(__CallIntNum=${CALLERID(num)})
	Set(CallStart=${STRFTIME(epoch,,%s)});

nagrywamy, kto inicjował połączenie i rejestrujemy czas rozpoczęcia rozmowy.

A po jego zakończeniu, w specjalnym kontekście h

h => {
        Set(CDR_PROP(disable)=true);
        Set(CallStop=${STRFTIME(epoch,,%s)});
        Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)});
	if(${ISNULL(${CallMeDISPOSITION})}) {
          Set(CallMeDISPOSITION=${CDR(disposition)});
        }
	System(curl -s http://pbx.vistep.ru/CallMeOut.php --data action=sendcall2b24 --data call_id=${CallMeCALL_ID} --data-urlencode FullFname=${FullFname} --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition=${CallMeDISPOSITION});
}

wyłączamy zapisywanie w tabeli CDR dla tego rozszerzenia (nie jest to konieczne), ustawiamy czas zakończenia rozmowy, obliczamy czas trwania, jeśli wynik rozmowy jest nieznany — ustawiamy (zmienna CallMeDISPOSITION) i na ostatnim kroku, wysyłamy wszystko do Bitrixa przez systemowy curl.

I jeszcze trochę magii — przychodzące połączenie:

888999 => {
	&recording(${CALLERID(number)},${EXTEN});
        Answer();
        ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp()); // ustawiamy CallerID, jeśli poznajemy go z Bitrix24
        Set(CallStart=${STRFTIME(epoch,,%s)}); // zaczynamy odliczanie czasu rozmowy
        Queue(Q1,tT);
        Set(CallMeDISPOSITION=${CDR(disposition)});
        Hangup();
        }

Tutaj interesuje nas tylko jeden wiersz.

ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp());

Mówi to, aby ATC ustawiło CallerID(name) na wartość zmiennej CallMeCallerIDName.

Sama zmienna CallMeCallerIDName, z kolei, jest ustawiana przez aplikację CallMe (jeśli w Bitrix24 są dane osoby dla numeru dzwoniącego — ustawiamy je jako CallerID(name), jeśli nie — nic nie zrobimy).

Konfiguracja aplikacji

Plik konfiguracyjny aplikacji — /var/www/pbx.vistep.ru/config.php

Opis parametrów aplikacji:

  • CallMeDEBUG — jeśli 1, to do pliku dziennika będą zapisywane wszystkie zdarzenia przetwarzane przez aplikację, 0 — nic nie zapisujemy
  • tech — SIP/PJSIP/IAX/etc
  • authToken — token autoryzacji Bitrix24, kod autoryzacji wychodzącego webhooka
  • bitrixApiUrl — URL przychodzącego webhooka, bez profile/
  • rozszerzenia — lista zewnętrznych numerów
  • context — kontekst dla inicjacji połączenia
  • timeout_listenera — prędkość przetwarzania zdarzeń z Asteriska
  • asterisk — tablica z ustawieniami połączenia z Asteriskiem:
  • Najciekawsza linia — ostatnia. — ip lub nazwa hosta serwera Asterisk
  • scheme — schemat połączenia (tcp://, tls://)
  • port — port
  • nazwa użytkownika — nazwa użytkownika
  • secret — hasło
  • timeout_connect — timeout połączenia
  • timeout_read — timeout odczytu

przykład pliku konfiguracyjnego:

 1, // debugowanie komunikatów w logu: 1 - zapisuj, 0 - nie zapisuj
        'tech' => 'SIP',
        'authToken' => 'xcrp2ylhzzd2v43cmfjqmkvrgrcbkni6', // token autoryzacji Bitrix
        'bitrixApiUrl' => 'https://b24-xsynia.bitrix24.ru/rest/1/7eh61lh8pahw0fwt/', // URL do API Bitrix (przychodzący webhook)
        'rozszerzenia' => array('888999'), // lista zewnętrznych numerów, oddzielonych przecinkiem
        'kontekst' => 'dial_out', // wychodzący kontekst dla inicjacji połączenia
        'asterisk' => array( // ustawienia do połączenia z Asterisk
                    'host' => '10.100.111.249',
                    'scheme' => 'tcp://',
                    'port' => 5038,
                    'username' => 'callme',
                    'secret' => 'JD3clEB8_f23r-3ry84gJ',
                    'timeout_connect' => 10000,
                    'timeout_read' => 10000
                ),
        'timeout_listenera' => 300, // prędkość przetwarzania zdarzeń z Asteriska

);

Konfiguracja supervisor

Supervisor służy do uruchamiania procesu obsługi zdarzeń z Asterisk CallMeIn.php, który monitoruje przychodzące połączenia i współpracuje z Bitrix24 (pokazywanie karty, ukrywanie karty itd.).

Plik konfiguracyjny, który należy utworzyć:

/etc/supervisord.d/callme.conf

[program:callme]
command=/usr/bin/php CallMeIn.php
directory=/var/www/pbx.vistep.ru
autostart=true
autorestart=true
startretries=5
stderr_logfile=/var/www/pbx.vistep.ru/logs/daemon.log
stdout_logfile=/var/www/pbx.vistep.ru/logs/daemon.log

Uruchomienie i restart aplikacji:

supervisorctl start callme
supervisorctl restart callme

sprawdzenie statusu pracy aplikacji:

supervisorctl status callme
callme                           RUNNING   pid 11729, uptime 17 days, 16:58:07

Podsumowanie

To stało się dość skomplikowane, ale jestem pewien, że doświadczony administrator będzie w stanie to wdrożyć i zadowolić swoich użytkowników.

Jak obiecałem, link do GitHuba.

Pytania, sugestie — proszę w komentarzach. Jeśli interesuje Cię, jak przebiegał rozwój tej integracji, napisz, a w kolejnym artykule postaram się opisać wszystko bardziej szczegółowo.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster