Nos ocupamos de FreePBX y lo integramos con Bitrix24 y no solo

Bitrix24 es un gran recolector que combina CRM, flujo de documentos, contabilidad y muchas otras cosas que a los gerentes realmente les gustan y al personal de TI no les gusta mucho. El portal es utilizado por muchas empresas pequeñas y medianas, incluidas pequeñas clínicas, trabajadores de fabricación e incluso salones de belleza. La función principal que los gerentes "adoran" es la integración de telefonía y CRM, cuando cualquier llamada se registra de inmediato en CRM, se crean tarjetas de cliente, se muestra información sobre el cliente cuando entra, y se puede ver de inmediato quién es, qué puede vender y cuánto debe. Pero la telefonía de Bitrix24 y su integración con CRM cuesta dinero, a veces mucho. En el artículo te contaré la experiencia de integración con herramientas abiertas y la popular IP PBX FreePBX , y también consideraré la lógica del trabajo de varias partes.

Trabajo como empresa de outsourcing en una empresa que vende y configura, integra telefonía IP. Cuando me preguntaron si podíamos ofrecer a esta y esta empresa algo para integrar Bitrix24 con las PBX que tienen los clientes, así como con las PBX virtuales en varias empresas de VDS, fui a Google. Y él, por supuesto, me dio un enlace a un artículo en Habr , donde hay una descripción y github, y todo parece funcionar. Pero al intentar usar esta solución, resultó que Bitrix24 no es lo que solía ser, y hay mucho que rehacer. Además, FreePBX no es un asterisco desnudo para usted, aquí debe pensar en cómo combinar la usabilidad y un plan de marcado duro en los archivos de configuración.

Estudiamos la lógica del trabajo

Entonces, primero, ¿cómo se supone que funcione? Cuando llega una llamada desde el exterior al PBX (evento SIP INVITE del proveedor), comienza el procesamiento del plan de marcado (plan de marcado): las reglas de qué hacer con la llamada y en qué orden. Se puede obtener mucha información del primer paquete, que luego se puede utilizar en las reglas. Una excelente herramienta para estudiar el interior de SIP es el analizador sngrep ( enlace ), que simplemente se instala en distribuciones populares a través de apt install / yum install y similares, pero también puede compilarlo desde la fuente. Veamos el registro de llamadas en sngrep

En una forma simplificada, el plan de marcación se ocupa solo del primer paquete, a veces durante la conversación, se transfieren llamadas, se presionan botones (DTMF), varias cosas interesantes como FollowMe, RingGroup, IVR y otros.

Que hay dentro del paquete Invite

DID CallerID. DID - , CallerID - .

- (/ ) (Ring Group), IVR (, ... ...), (Phrases), (Time Conditions), (FollowMe, Forward). , .

"". Asterisk - , ( exten, exten=DID). - ( - Dial(), - Hangup()), (IF, ELSE, ExecIF ), (Goto, GotoIF), (Gosub, Macro). include _, . , include .

FreePBX include Gosub, Macro Handler. FreePBX

, (Macro), (Gosub) (Goto), , .

. DID, , - . 1 . hangupcall, , (hangup handler).

CRM, , CRM?

CRM? , . API, API HTTP REST. asterisk.

Asterisk :

AMI

Event: Newchannel Privilege: call,all Channel: PJSIP/VMS_pjsip-0000078b ChannelState: 4 ChannelStateDesc: Ring CallerIDNum: 111222 CallerIDName: 111222 ConnectedLineNum: ConnectedLineName: Language: en AccountCode: Context: from-pstn Exten: s Priority: 1 Uniqueid: 1599589046.5244 Linkedid: 1599589046.5244

ARI

{ "variable":"CallMeCallerIDName", "value":"111222", "type":"ChannelVarset", "timestamp":"2020-09-09T09:38:36.269+0000", "channel":{ "id":"1599644315.5334", "name":"PJSIP/VMSpjsip-000007b6", "state":"Ring", "caller":{ "name":"111222", "number":"111222" }, "connected":{ "name":"", "number":"" }, "accountcode":"", "dialplan":{ "context":"from-pstn", "exten":"s", "priority":2, "appname":"Stasis", "appdata":"hello-world" }, "creationtime":"2020-09-09T09:38:35.926+0000", "language":"ru" }, "asteriskid":"48:5b:aa:aa:aa:aa", "application":"hello-world" }

, API , . CRM :

  • , , CallerID, DID, , ( CRM)

  • , ,

  • ( ), ,

  • : CRM, FollowME ( CRM)

AMI ARI, ARI , , , AMI ( , , ). , - AMI ( ). ( , ) - ( ) PAMI. * ARI, .

, FreePBX AMI , , , , , - . PAMI -.

(s - , DID)

[ext-did-custom]

exten => s,1,Set(CallStart=${STRFTIME(epoch,,%s)})
AMI

Event: Newchannel

Privilege: call,all

Channel: PJSIP/VMS_pjsip-0000078b

ChannelState: 4

ChannelStateDesc: Ring

CallerIDNum: 111222

CallerIDName: 111222

ConnectedLineNum:

ConnectedLineName:

Language: en

AccountCode:

Context: from-pstn

Exten: s

Priority: 1

Uniqueid: 1599589046.5244

Linkedid: 1599589046.5244

Application: Set AppData:

CallStart=1599571046

FreePBX extention.conf extention_additional.conf, extention_custom.conf

extention_custom.conf
[globals]	
;;       -  asterisk     
;;    
WAV=/var/www/html/callme/records/wav 
MP3=/var/www/html/callme/records/mp3

;;        
URLRECORDS=https://www.host.ru/callmeplus/records/mp3

;;      
URLPHP=https://www.host.ru/callmeplus

;;   
RECORDING=1

;;        . 
;;     ,      - 
;;  
[recording]
exten => ~~s~~,1,Set(LOCAL(calling)=${ARG1})
exten => ~~s~~,2,Set(LOCAL(called)=${ARG2})
exten => ~~s~~,3,GotoIf($["${RECORDING}" = "1"]?4:14)
exten => ~~s~~,4,Set(fname=${UNIQUEID}-${STRFTIME(${EPOCH},,%Y-%m-%d-%H_%M)}-${calling}-${called})
exten => ~~s~~,5,Set(datedir=${STRFTIME(${EPOCH},,%Y/%m/%d)})
exten => ~~s~~,6,System(mkdir -p ${MP3}/${datedir})
exten => ~~s~~,7,System(mkdir -p ${WAV}/${datedir})
exten => ~~s~~,8,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")
exten => ~~s~~,9,Set(FullFname=${URLRECORDS}/${datedir}/${fname}.mp3)
exten => ~~s~~,10,Set(CDR(filename)=${fname}.mp3)
exten => ~~s~~,11,Set(CDR(recordingfile)=${fname}.wav)
exten => ~~s~~,12,Set(CDR(realdst)=${called})
exten => ~~s~~,13,MixMonitor(${WAV}/${datedir}/${fname}.wav,b,${monopt})
exten => ~~s~~,14,NoOp(Finish if_recording_1)
exten => ~~s~~,15,Return()


;;      
[ext-did-custom]

;;  ,     ,   -    '8'
exten =>  s,1,Set(CALLERID(num)=8${CALLERID(num)})

;;     
exten =>  s,n,Gosub(recording,~~s~~,1(${CALLERID(number)},${EXTEN}))
exten =>  s,n,ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp())
exten =>  s,n,Set(CallStart=${STRFTIME(epoch,,%s)})
exten =>  s,n,Set(CallMeDISPOSITION=${CDR(disposition)})

;;  !   . 
;;      (exten=>h,1,)  FreePBX   - Macro(hangupcall,)  . 
;;   Hangup_Handler   
exten => s,n,Set(CHANNEL(hangup_handler_push)=sub-call-from-cid-ended,s,1(${CALLERID(num)},${EXTEN}))

;;    
[sub-call-from-cid-ended]

;;      
exten => s,1,Set(CDR_PROP(disable)=true)
exten => s,n,Set(CallStop=${STRFTIME(epoch,,%s)})
exten => s,n,Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)})

;;   - ,  ...
exten => s,n,Set(CallMeDISPOSITION=${CDR(disposition)})
exten => s,n,Return


;;    -  
[outbound-allroutes-custom]

;; 
exten => _.,1,Gosub(recording,~~s~~,1(${CALLERID(number)},${EXTEN}))
;; 
exten => _.,n,Set(__CallIntNum=${CALLERID(num)})
exten => _.,n,Set(CallExtNum=${EXTEN})
exten => _.,n,Set(CallStart=${STRFTIME(epoch,,%s)})
exten => _.,n,Set(CallmeCALLID=${SIPCALLID})

;;  Hangup_Handler   
exten => _.,n,Set(CHANNEL(hangup_handler_push)=sub-call-internal-ended,s,1(${CALLERID(num)},${EXTEN}))

;;    
[sub-call-internal-ended]

;; 
exten => s,1,Set(CDR_PROP(disable)=true)
exten => s,n,Set(CallStop=${STRFTIME(epoch,,%s)})
exten => s,n,Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)})
exten => s,n,Set(CallMeDISPOSITION=${CDR(disposition)})

;;  ,      CRM -  , 
;;     
exten => s,n,System(curl -s ${URLPHP}/CallMeOut.php --data action=sendcall2b24 --data ExtNum=${CallExtNum} --data call_id=${SIPCALLID} --data-urlencode FullFname='${FullFname}' --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition='${CallMeDISPOSITION}')
exten => s,n,Return

-

  • .conf, FreePBX ( .ael, )

  • exten=>h hangup_handler, FreePBX

  • , ExtNum

  • _custom FreePBX - [ext-did-custom], [outbound-allroutes-custom]

  • -

AMI - FreePBX _custom

manager_custom.conf
;;   
[callmeplus]
;;  
secret = trampampamturlala
deny = 0.0.0.0/0.0.0.0

;;      -   ,    
permit = 127.0.0.1/255.255.255.255
read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan
write = system,call,agent,log,verbose,user,config,command,reporting,originate

/etc/asterisk, ( )

# astrisk -rv
  Connected to Asterisk 16.6.2 currently running on freepbx (pid = 31629)
#freepbx*CLI> dialplan reload
     Dialplan reloaded.
#freepbx*CLI> exit

PHP

24, AMI , . AMI . , . , PAMI , , ..

, NewExten [from-pstn], . _custom CallMeCallerIDName CallStart

  1. UserID, , . ? , ( ) ? Fisrt Available, , .

  2. 24, CallID, . UserID

, (, , ), mp3 ( ).

CallMeIn.php , SystemD callme.service, /etc/systemd/system/callme.service

[Unit]
Description=CallMe

[Service]
WorkingDirectory=/var/www/html/callmeplus
ExecStart=/usr/bin/php /var/www/html/callmeplus/CallMeIn.php 2>&1 >>/var/log/callmeplus.log
ExecStop=/bin/kill -WINCH ${MAINPID}
KillSignal=SIGKILL

Restart=on-failure
RestartSec=10s

#  ,   
#User=www-data  #Ubuntu - debian
#User=nginx #Centos

[Install]
WantedBy=multi-user.target

systemctl service

# systemctl enable callme
# systemctl start callme

( ). , php ( FeePBX). ( https) .

. CallMeOut.php :

  • php ( "" ). , HTTP POST,

  • , . Asterisk [sub-call-internal-ended]

- ( HTTPS) CallMeOut.php. FreePBX, /var/www/html, .

(, , ). , FreeDomain( https://www.freenom.com/ru/index.html), IP ( 80, 443 , ). DNS , ( 15 48 ) . - 1 .

github , . - , , , . (

Docker

- Docker - , , ( LetsEncrypt , , FreePBX ( - 88), LetsEncrypt

( git clone), ( asterisk) URL

version: '3.3'
services:
  nginx:
    image: nginx:1.15-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/ssl_docker.conf:/etc/nginx/conf.d/ssl_docker.conf
  certbot:
    image: certbot/certbot
  freepbx:
    image: flaviostutz/freepbx
    ports:
      - 88:80 #  
      - 5060:5060/udp
      - 5160:5160/udp
      - 127.0.0.1:5038:5038 #  CallMeOut.php
#      - 3306:3306
      - 18000-18100:18000-18100/udp
    restart: always
    environment:
      - ADMIN_PASSWORD=admin123
    volumes:
      - backup:/backup
      - recordings:/var/spool/asterisk/monitor
      - ./callme:/var/www/html/callme
      - ./systemd/callme.service:/etc/systemd/system/callme.conf
      - ./asterisk/manager_custom.conf:/etc/asterisk/manager_custom.conf
      - ./asterisk/extensions_custom.conf:/etc/asterisk/extensions_custom.conf
#      - ./conf/startup.sh:/startup.sh

volumes:
  backup:
  recordings:

docker-compose.yaml,

docker-compose up -d

nginx , nginx/ssl_docker.conf

CRM , . API CRM, - ShugarCRM Vtiger, ! , . , .

: ,




All Articles