Integration von Asterisk und Bitrix24

Integration von Asterisk und Bitrix24
Im Internet gibt es verschiedene Integrationsmöglichkeiten fĂŒr die IP-Telefonanlage Asterisk und das CRM Bitrix24, aber wir haben uns dennoch entschieden, unsere eigene Lösung zu schreiben.

Die FunktionalitĂ€t ist ganz standardmĂ€ĂŸig:

  • Mit einem Klick auf den Link mit der Telefonnummer des Kunden in Bitrix24 verbindet Asterisk die interne Nummer des Benutzers, der auf den Link geklickt hat, mit der Telefonnummer des Kunden. In Bitrix24 wird ein Anrufprotokoll aufgezeichnet und nach dem Ende des Anrufs wird das GesprĂ€chsprotokoll abgerufen.
  • Ein Anruf wird extern an Asterisk weitergeleitet - im Bitrix24-Interface zeigen wir die Kundendaten dem Mitarbeiter an, auf dessen Nummer der Anruf eingegangen ist.
    Falls es einen solchen Kunden nicht gibt, öffnen wir das Formular zur Erstellung eines neuen Leads.
    Sobald der Anruf beendet ist, reflektieren wir dies in der Karte und rufen das GesprÀchsprotokoll ab.

Unter dem Beitrag erklÀre ich, wie man alles auf seinem Server einrichtet, und gebe einen Link zu GitHub - ja, ja, holt es euch und nutzt es!

Allgemeine Beschreibung

Unsere Integration haben wir CallMe genannt. CallMe ist eine kleine Webanwendung, die in PHP geschrieben wurde.

Verwendete Technologien und Dienste

  • PHP 5.6
  • PHP AMI-Bibliothek
  • Composer
  • Nginx + php-fpm
  • Supervisor
  • AMI (Asterisk Manager Interface)
  • Webhooks von Bitrix (vereinfachte Implementierung von REST API)

Vorbereitende Konfiguration

Auf dem Server mit Asterisk muss ein Webserver installiert werden (bei uns ist es nginx+php-fpm), Supervisor und git.

Befehl zur Installation (CentOS):

yum install nginx php-fpm supervisor git

Wir wechseln in das Verzeichnis, das fĂŒr den Webserver zugĂ€nglich ist, ziehen die Anwendung aus git und setzen die notwendigen Berechtigungen fĂŒr den Ordner:


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

Danach konfigurieren wir nginx, unsere Konfiguration befindet sich in

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

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

server {
#        listen *:80;
#	server_name pbx.vistep.de;


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

    listen 443 ssl http2;
    server_name pbx.vistep.de;
    resolver 8.8.8.8;
    ssl_stapling on;
    ssl on;
    ssl_certificate /etc/letsencrypt/live/pbx.vistep.de/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/pbx.vistep.de/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; # Zugriff auf versteckte Dateien verboten
        }

        location ~* \/(?:uploads|files)\/.*.php$ {
                deny all; # Zugriff auf hochgeladene Skripte verboten
        }

        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; # Statik Caching
        }

	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;
		}
}

Die Analyse der Konfiguration, Sicherheitsfragen, das Erlangen des Zertifikats und sogar die Auswahl des Webservers bleiben hier unerwĂ€hnt – darĂŒber wurde bereits viel geschrieben. Die Anwendung hat keine EinschrĂ€nkungen, sie funktioniert sowohl ĂŒber http als auch ĂŒber https.

Wir haben https, das Zertifikat von Let's Encrypt.

Wenn Sie alles richtig gemacht haben, sollten Sie beim Klicken auf den Link etwas Ähnliches sehen.

Integration von Asterisk und Bitrix24

Konfiguration von Bitrix24

Lassen Sie uns zwei Webhooks erstellen.

Eingehender Webhook.

Unter dem Administratorkonto (mit ID 1) gehen wir zu: Anwendungen -> Webhooks -> Webhook hinzufĂŒgen -> Eingehender Webhook

Integration von Asterisk und Bitrix24

FĂŒllen Sie die Parameter des eingehenden Webhooks wie auf den Screenshots aus:

Integration von Asterisk und Bitrix24

Integration von Asterisk und Bitrix24

Und klicken Sie auf Speichern.

Nach dem Speichern stellt Bitrix24 die URL des eingehenden Webhooks zur VerfĂŒgung, z. B.:

Integration von Asterisk und Bitrix24

Speichern Sie Ihre Variante der URL ohne den abschließenden /profile/ – sie wird in der Anwendung zur Bearbeitung eingehender Anrufe verwendet.

Das wĂ€re fĂŒr mich https://b24-xsynia.bitrix24.ru/rest/1/7eh61lh8pahw0fwt/

Ausgehender Webhook.

Anwendungen -> Webhooks -> Webhook hinzufĂŒgen -> Ausgehender Webhook

Die Details wieder auf den Screenshots:

Integration von Asterisk und Bitrix24

Integration von Asterisk und Bitrix24

Speichern und den Autorisierungscode erhalten.

Integration von Asterisk und Bitrix24

Das wĂ€re fĂŒr mich xcrp2ylhzzd2v43cmfjqmkvrgrcbkni6. Dies muss ebenfalls kopiert werden, da es fĂŒr ausgehende Anrufe benötigt wird.

Wichtig!

Auf dem Bitrix24-Server muss ein SSL-Zertifikat eingerichtet sein (Letsencrypt kann verwendet werden), andernfalls funktioniert die Bitrix-API nicht. Wenn Sie die Cloud-Version nutzen, brauchen Sie sich keine Sorgen zu machen — dort ist bereits ein SSL-Zertifikat vorhanden.

Wichtig!

Im Feld "Handler-Adresse" muss eine aus dem Internet zugÀngliche Adresse angegeben werden!

Und als letzten Schliff installieren wir unsere CallMeOut-App fĂŒr Telefonanrufe (damit beim Klicken auf eine Nummer im PBX ein Befehl zur Initiierung des Anrufs gesendet wird).

Im MenĂŒ wĂ€hlen wir: Weitere -> Telefonie -> Weitere -> Einstellungen, setzen im Feld "Standardnummer fĂŒr ausgehende Anrufe" die App: CallMeOut und klicken auf "Speichern".

Integration von Asterisk und Bitrix24

Asterisk-Konfiguration

FĂŒr eine erfolgreiche Interaktion zwischen Asterisk und Bitrix24 mĂŒssen wir den AMI-Benutzer callme in die manager.conf hinzufĂŒgen:

[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

Es gibt noch einige Tricks, die wir im Dialplan (bei uns extensions.ael) implementieren mĂŒssen.

Ich fĂŒhre die gesamte Datei auf, danach gebe ich ErklĂ€rungen:

globals {
    WAV=\/var\/www\/pbx.vistep.ru\/callme\/records\/wav; \/\/ TemporĂ€res Verzeichnis fĂŒr WAV
    MP3=\/var\/www\/pbx.vistep.ru\/callme\/records\/mp3; \/\/ Wo die mp3-Dateien gespeichert werden
    URLRECORDS=https:\/\/pbx.vistep.ru\/callme\/records\/mp3;
    RECORDING=1; \/\/ Aufnahme, 1 - aktiviert.
};

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()); \/\/ setzen CallerID basierend auf Information von 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 war bereits gesetzt ===));  
}

}


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.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});
}

};

Lass uns von vorne beginnen: die Direktive globals.

Variable URLRECORDS speichert die URL zu den Aufzeichnungsdateien der GesprÀche, von der Bitrix24 die Dateien in das Kontaktformular ziehen wird.

Als nÀchstes interessiert uns das Makro recording.

Hier, neben der Aufnahme der GesprÀche, setzen wir die Variable FullFname.

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

Sie speichert die vollstĂ€ndige URL zu einer bestimmten Datei (das Makro wird ĂŒberall aufgerufen).

Lass uns den ausgehenden Anruf durchgehen:

_. => {
	&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});
}

Angenommen, wir rufen die 89991234567 an, zunÀchst landen wir hier:

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

d.h. das Makro zur Aufzeichnung des GesprÀchs wird aufgerufen und die erforderlichen Variablen gesetzt.

Dann

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

wir erfassen, wer den Anruf initiiert hat, und protokollieren die Startzeit des Anrufs.

Und bei dessen Abschluss, im speziellen Kontext 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});
}

wir deaktivieren die Aufzeichnung in der CDR-Tabelle fĂŒr diese Durchwahl (es ist nicht nötig), setzen die Abschlusszeit des Anrufs und berechnen die Dauer, wenn das Ergebnis des Anrufs unbekannt ist — setzen wir (Variable CallMeDISPOSITION) und als letzten Schritt senden wir alles ĂŒber einen systemeigenen curl an Bitrix.

Und noch ein bisschen Magie — eingehender Anruf:

888999 => {
	&recording(${CALLERID(number)},${EXTEN});
        Answer();
        ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp()); // wir setzen CallerID, wenn wir ihn von Bitrix24 erhalten haben
        Set(CallStart=${STRFTIME(epoch,,%s)}); // wir starten den Zeitmesser fĂŒr den Anruf
        Queue(Q1,tT);
        Set(CallMeDISPOSITION=${CDR(disposition)});
        Hangup();
        }

Hier interessiert uns nur eine Zeile.

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

Sie sagt der TK-Anlage, den CallerID(name) auf die Variable CallMeCallerIDName.

Die Variable CallMeCallerIDName wird wiederum von der Anwendung CallMe gesetzt (wenn in Bitrix24 ein Name fĂŒr die Anrufernummer vorhanden ist — setzen wir ihn als CallerID(name), andernfalls unternehmen wir nichts).

Konfiguration der Anwendung

Die Konfigurationsdatei der Anwendung — /var/www/pbx.vistep.ru/config.php

Beschreibung der Anwendungsparameter:

  • CallMeDEBUG — wenn 1, werden alle vom Programm verarbeiteten Ereignisse in die Protokolldatei geschrieben, 0 — nichts wird geschrieben
  • tech — SIP/PJSIP/IAX/etc.
  • authToken — Token zur Autorisierung Bitrix24, Autorisierungscode des ausgehenden Webhooks
  • bitrixApiUrl — URL des eingehenden Webhooks, ohne profile/
  • extentions — Liste externer Nummern
  • context — Kontext fĂŒr die Ursprungserstellung des Anrufs
  • listener_timeout — Geschwindigkeit der Ereignisverarbeitung von Asterisk
  • asterisk — Array mit Verbindungseinstellungen zu Asterisk:
  • host — IP oder Hostname des Asterisk-Servers
  • scheme — Verbindungsprotokoll (tcp://, tls://)
  • port — Port
  • username — Benutzername
  • secret — Passwort
  • connect_timeout — VerbindungszeitĂŒberschreitung
  • read_timeout — LesezeitĂŒberschreitung

Beispiel fĂŒr eine Konfigurationsdatei:

 1, // Debugging-Nachrichten im Protokoll: 1 - schreiben, 0 - nicht schreiben
        'tech' => 'SIP',
        'authToken' => 'xcrp2ylhzzd2v43cmfjqmkvrgrcbkni6', // Token zur Autorisierung bei Bitrix
        'bitrixApiUrl' => 'https://b24-xsynia.bitrix24.ru/rest/1/7eh61lh8pahw0fwt/', // URL zur Bitrix API (eingehender Webhook)
        'extentions' => array('888999'), // Liste externer Nummern, durch Kommas getrennt
        'context' => 'dial_out', // Ausgangskontext fĂŒr die Ursprungserstellung des Anrufs
        'asterisk' => array( // Einstellungen zur Verbindung zu Asterisk
                    'host' => '10.100.111.249',
                    'scheme' => 'tcp://',
                    'port' => 5038,
                    'username' => 'callme',
                    'secret' => 'JD3clEB8_f23r-3ry84gJ',
                    'connect_timeout' => 10000,
                    'read_timeout' => 10000
                ),
        'listener_timeout' => 300, // Geschwindigkeit der Ereignisverarbeitung von Asterisk

);

Supervisor-Konfiguration

Supervisor dient zum Starten des Ereignisverarbeitungsprozesses von Asterisk CallMeIn.php, der eingehende Anrufe ĂŒberwacht und mit Bitrix24 interagiert (Karte anzeigen, Karte ausblenden usw.).

Die zu erstellende Konfigurationsdatei lautet:

/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

Start und Neustart der Anwendung:

supervisorctl start callme
supervisorctl restart callme

Status der Anwendung ĂŒberprĂŒfen:

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

Fazit

Es war ziemlich komplex, aber ich bin mir sicher, dass ein erfahrener Administrator dies implementieren und seine Benutzer begeistern kann.

Wie versprochen, Link zu GitHub.

Fragen oder WĂŒnsche — bitte in die Kommentare. Wenn Sie auch daran interessiert sind, wie die Entwicklung dieser Integration verlief, schreiben Sie es, und in meinem nĂ€chsten Artikel werde ich alles detaillierter erklĂ€ren.

Quelle: habr.com

60GB SSD 8Gb DDR4