jest ogromnym kombajnem, który łączy w sobie CRM, obieg dokumentów, księgowość oraz jeszcze wiele innych funkcji, które bardzo podobają się menedżerom, ale niekoniecznie IT. Portal ten wykorzystywany jest przez wiele małych i średnich firm, w tym niewielkie kliniki, producentów oraz nawet salony piękności. Główną funkcją, którą "uwielbiają" menedżerowie, jest integracja telefonii i CRM, dzięki czemu każde połączenie jest automatycznie rejestrowane w CRM, tworzone są karty klientów, a przy połączeniu wyświetlana jest informacja o kliencie, co pozwala od razu wiedzieć, kim on jest, co można mu sprzedać i ile jest winien. Jednak telefonia z Bitrix24 i jej integracja z CRM wiąże się z kosztami, czasem niemałymi. W artykule przedstawię doświadczenie integracji z otwartymi narzędziami oraz popularną IP PBX. , a także omówię logikę działaniu różnych części.
Pracuję na zlecenie w firmie, która zajmuje się sprzedażą, konfiguracją oraz integracją telefonii IP. Gdy zapytano mnie, czy możemy tej i tamtej firmie zaproponować coś do integracji Bitrix24 z PBX, które są u klientów oraz z wirtualnymi PBX na różnych VDS firmy, poszłem do Google. Oczywiście, znalazłem link do , gdzie znajduje się opis, i GitHub, i wszystko wygląda na działające. Jednak przy próbie skorzystania z tego rozwiązania okazało się, że Bitrix24 już nie jest tym samym, co wcześniej i trzeba wiele zmienić. Ponadto, FreePBX to nie jest goły Asterisk, trzeba pomyśleć, jak połączyć wygodę użytkowania z hardcore'owym dialplanem w plikach konfiguracyjnych.
Analizujemy logikę działania
Zatem najpierw, jak to wszystko powinno działać. Przy przyjściu połączenia zewnętrznego do PBX (zdarzenie SIP INVITE od dostawcy) rozpoczyna się przetwarzanie dialplanu — reguł, co i w jakiej kolejności zrobić z połączeniem. Z pierwszego pakietu można uzyskać wiele informacji, które następnie można wykorzystać w regułach. Doskonałym narzędziem do badania wnętrz SIP jest analizator sngrep () który wystarczy zainstalować w popularnych dystrybucjach przez apt install/yum install i tym podobne, ale można go również skompilować ze źródeł. Przyjrzyjmy się logowi połączenia w sngrep.

W uproszczonej formie plan dialowy zajmuje się tylko pierwszym pakietem, czasami w trakcie rozmowy dochodzi również do przekazywania połączeń, naciśnięć przycisków (DTMF), różne ciekawe elementy takie jak FollowMe, RingGroup, IVR i inne.
Co znajduje się w pakiecie Invite

Większość prostych planów dialowych działa z pierwszymi dwoma polami i cała logika kręci się wokół DID i CallerID. DID — dokąd dzwonimy, CallerID — kto dzwoni.
Ale przecież mamy firmę, a nie jeden telefon — więc w centrali prawdopodobnie są grupy dzwonienia (jednoczesne/podrzędne dzwonienie kilku aparatów) na numery stacjonarne (Ring Group), IVR (Witaj, dzwonisz do... Naciśnij jeden dla…), automatyczne sekretarki (Phrases), warunki czasowe (Time Conditions), przekierowywanie na inne numery lub na telefon komórkowy (FollowMe, Forward). Oznacza to, że jednoznaczne określenie, do kogo tak naprawdę trafi połączenie i z kim będzie rozmowa przy nadejściu połączenia, jest bardzo trudne. Oto przykład początku typowego połączenia w centrali naszych klientów.

Po pomyślnym odebraniu połączenia w centrali następuje jego podróż przez plan dialowy w różnych „kontekstach”. Kontekst z punktu widzenia Asteriska to numerowany zestaw poleceń, z których każde zawiera filtr według wybieranego numeru (nazywa się exten, dla zewnętrznego połączenia na początkowym etapie exten=DID). W linii planu dialowego mogą znajdować się wszelkie dowolne komendy — wewnętrzne funkcje (na przykład zadzwonić do wewnętrznego abonenta — Dial(), odłożyć słuchawkę — Hangup()), operatory warunkowe (IF, ELSE, ExecIF i podobne), przejścia do innych zasad tego kontekstu (Goto, GotoIF), przejścia do innych kontekstów w formie wywołania funkcji (Gosub, Macro). Osobno znajduje się dyrektywa include nazwa_kontekstu, , która dodaje komendy innego kontekstu na koniec aktualnego kontekstu. Komendy, dołączone przez include, zawsze są wykonywane po komendach aktualnego kontekstu.
Cała logika działania FreePBX opiera się na wzajemnym włączaniu różnych kontekstów przez include oraz wywołaniu przez Gosub, Macro i procesory Handler. Przeanalizujmy kontekst połączeń przychodzących FreePBX.

Połączenie przechodzi przez wszystkie konteksty od góry do dołu po kolei, w każdym kontekście mogą być wywołania innych kontekstów jako makra (Macro), funkcje (Gosub) lub po prostu przejścia (Goto), dlatego rzeczywiste drzewo tego, co jest wywoływane, można śledzić jedynie w logach.
Typowy schemat konfiguracji typowej biurowej centrali PBX przedstawiono poniżej. Podczas połączeń przychodzących wyszukiwany jest DID, a następnie sprawdzane są warunki czasowe; jeśli wszystko jest w porządku, uruchamiane jest menu głosowe. Z niego przyciskiem 1 lub po czasie oczekiwania następuje przejście do grupy operatorów. Po zakończeniu rozmowy wywoływany jest makro hangupcall, po którym nie będzie możliwe wykonanie żadnych czynności w dialplanu, z wyjątkiem specjalnych obhandlerów (hangup handler).

Gdzie w tym algorytmie połączenia powinniśmy dostarczyć informacje o rozpoczęciu połączenia do CRM, gdzie rozpocząć nagrywanie, gdzie zakończyć nagrywanie i wysłać je razem z informacjami o połączeniu do CRM?
Integracja z systemami zewnętrznymi
Czym jest integracja PBX i CRM? To ustawienia i programy, które konwertują dane i zdarzenia pomiędzy tymi dwiema platformami i przesyłają je nawzajem. Najpowszechniejszym sposobem komunikacji między niezależnymi systemami jest API, a najpopularniejszym sposobem dostępu do API jest HTTP REST. Jednak nie w przypadku asterisk.
Wewnątrz Asteriska znajdują się:
AGI — synchroniczne wywołanie zewnętrznych programów/komponentów, używane głównie w dialplanach, dostępne są biblioteki takie jak ,
AMI — tekstowy soket TCP, działający na zasadzie subskrypcji zdarzeń i wprowadzania tekstowych poleceń, przypominający SMTP od wewnątrz, potrafi monitorować zdarzenia i zarządzać połączeniami, dostępna jest biblioteka — najbardziej popularna do nawiązywania połączeń z Asteriskiem
Przykład wyjścia AMI
Zdarzenie: Newchannel
Uprawnienie: call,all
Kanał: PJSIP/VMS_pjsip-0000078b
StanKanału: 4
OpisStanuKanału: Ring
NumerIDZgłaszającego: 111222
NazwaIDZgłaszającego: 111222
NumerLiniiPołączonej:
NazwaLiniiPołączonej:
Język: en
KodKonta:
Kontext: from-pstn
Exten: s
Priorytet: 1
UnikalnyID: 1599589046.5244
PowiązanyID: 1599589046.5244
ARI — połączenie obu, wszystko przez REST, WebSocket, w formacie JSON — ale z nowymi bibliotekami i wrapperami jest kiepsko, na szybko znaleziono (, ) które stagnowały w rozwoju około 3 lata temu.
Przykład wyjścia ARI przy inicjacji połączenia
{ "variable":"CallMeCallerIDName", "value":"111222", "type":"ChannelVarset", "timestamp":"2020-09-09T09:38:36.269+0000", "channel":{"id":"1599644315.5334", "name":"PJSIP/VMS pjsip-000007b6", "state":"Ring", "caller":{"name":"111222", "number":"111222"}, "connected":{"name":"", "number":""}, "accountcode":"", "dialplan":{"context":"from-pstn", "exten":"s", "priority":2, "app"name":"Stasis", "appdata":"hello-world" }, "creationtime":"2020-09-09T09:38:35.926+0000", "language":"ru" }, "asterisk"id":"48:5b:aa:aa:aa:aa", "application":"hello-world" }id»:»48:5b:aa:aa:aa:aa», «application»:»hello-world» }
Wygodność lub niewygodność, możliwość lub niemożność pracy z danym API zależą od problemów, które należy rozwiązać. Zadania do integracji z CRM są następujące:
Śledzenie początku połączenia, dokąd zostało przekierowane, wyciągnięcie CallerID, DID, czasy rozpoczęcia i zakończenia, a także być może dane z katalogu (aby znaleźć powiązanie telefonu z użytkownikiem CRM)
Rozpoczęcie i zakończenie nagrania rozmowy, zapis w potrzebnym formacie, powiadomienie po zakończeniu nagrania, gdzie znajduje się plik
Inicjowanie połączenia na podstawie zdarzenia zewnętrznego (z programu), zadzwonienie na wewnętrzny numer, zewnętrzny i połączenie ich
Opcjonalnie: integracja z CRM, grupami połączeń i FollowME dla automatycznego przekazywania rozmów przy braku obecności (według informacji CRM)
Wszystkie te zadania można rozwiązać poprzez AMI lub ARI, jednak ARI dostarcza znacznie mniej informacji, wiele zdarzeń nie jest śledzonych, wiele zmiennych, które są dostępne w AMI, nie są śledzone (na przykład wywołania makr, przypisywanie zmiennych wewnątrz makr, w tym nagrania rozmów). Dlatego, dla prawidłowego i dokładnego śledzenia — na razie wybierzemy AMI (ale nie ostatecznie). Co więcej (no a gdzież bez tego, jesteśmy leniwi) — w pierwotnej pracy () korzystają z PAMI. *Później trzeba spróbować przepisać na ARI, ale nie ma pewności, że się uda.
Wymyślamy integrację na nowo
Aby nasz FreePBX mógł w prosty sposób komunikować się z AMI na temat rozpoczęcia połączenia, czasu zakończenia, numerów, nazw zapisanych plików, obliczać czas trwania połączenia, najłatwiej skorzystać z tego samego triku, co pierwotni autorzy — wprowadzić własne zmienne i analizować wyniki pod kątem ich obecności. PAMI oferuje to w prosty sposób poprzez funkcję filtra.
Oto przykład przypisania własnej zmiennej dla czasu rozpoczęcia połączenia (s — to specjalny numer w planie wybierania, który jest wykonywany PRZED rozpoczęciem wyszukiwania po DID)
[ext-did-custom]
exten => s,1,Set(CallStart=${STRFTIME(epoch,,%s)})Przykład zdarzenia AMI dla tego wiersza
Zdarzenie: Newchannel
Uprawnienie: call,all
Kanał: PJSIP/VMS_pjsip-0000078b
StanKanału: 4
OpisStanuKanału: Ring
NumerIDZgłaszającego: 111222
NazwaIDZgłaszającego: 111222
NumerLiniiPołączonej:
NazwaLiniiPołączonej:
Język: en
KodKonta:
Kontext: from-pstn
Exten: s
Priorytet: 1
UnikalnyID: 1599589046.5244
PowiązanyID: 1599589046.5244
Aplikacja: Ustaw AppData:
CallStart=1599571046
Ponieważ FreePBX nadpisuje pliki extention.conf i extention_additional.conf, będziemy używać pliku extention_custom.conf
Pełny kod extention_custom.conf
[globals]
;; Sprawdź ścieżki i uprawnienia do folderów - użytkownik asterisk musi mieć prawo do zapisu
;; Tutaj będą zapisywane rozmowy
WAV=\/var\/www\/html\/callme\/records\/wav
MP3=\/var\/www\/html\/callme\/records\/mp3
;; Na tych ścieżkach będą odtwarzane i pobierane nagrania
URLRECORDS=https:\/\/www.host.ru\/callmeplus\/records\/mp3
;; Adres do callbacku przy wychodzącym połączeniu
URLPHP=https:\/\/www.host.ru\/callmeplus
;; Tak, nagrywamy rozmowy
RECORDING=1
;; To makro do nagrania rozmów w naszym folderze.
;; Można użyć także systemowego nagrania, ale na razie niech tak będzie -
;; działa
[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()
;; To jest główny kontekst do rozpoczęcia rozmowy
[ext-did-custom]
;; To jest psotność, robić to w ten sposób, ale działa - dodajemy do numeru '8'
exten => s,1,Set(CALLERID(num)=8${CALLERID(num)})
;; Tutaj różne zmienne dla skryptu
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)})
;; Najważniejsze! Obsługiwacz zakończenia rozmowy.
;; Zwykłe ścieżki obsługi zakończenia przez (exten=>h,1,cośtutaj) w FreePBX nie działają - Macro(hangupcall,) wszystko psuje.
;; Dlatego dodajemy Hangup_Handler do zakończenia połączenia
exten => s,n,Set(CHANNEL(hangup_handler_push)=sub-call-from-cid-ended,s,1(${CALLERID(num)},${EXTEN}))
;; Obsługiwacz zakończenia przychodzącego połączenia
[sub-call-from-cid-ended]
;; Informujemy o wartościach przy końcu połączenia
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)})
;; Status połączenia - Odpowiedziano, nie odpowiedziano...
exten => s,n,Set(CallMeDISPOSITION=${CDR(disposition)})
exten => s,n,Return
;; Obsługiwacz wychodzących połączeń - wszystko analogicznie
[outbound-allroutes-custom]
;; Nagranie
exten => _.,1,Gosub(recording,~~s~~,1(${CALLERID(number)},${EXTEN}))
;; Zmienne
exten => _.,n,Set(__CallIntNum=${CALLERID(num)})
exten => _.,n,Set(CallExtNum=${EXTEN})
exten => _.,n,Set(CallStart=${STRFTIME(epoch,,%s)})
exten => _.,n,Set(CallmeCALLID=${SIPCALLID})
;; Dodajemy Hangup_Handler do zakończenia połączenia
exten => _.,n,Set(CHANNEL(hangup_handler_push)=sub-call-internal-ended,s,1(${CALLERID(num)},${EXTEN}))
;; Obsługiwacz zakończenia wychodzącego połączenia
[sub-call-internal-ended]
;; zmienne
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)})
;; Wywołanie skryptu, który powiadomi o połączeniu w CRM - to wychodzące,
;; więc po fakcie zakończenia
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,ReturnCechą wyróżniającą się od oryginalnego dialplanu autorów artykułu źródłowego jest
Dialplan w formacie .conf, ponieważ tego wymaga FreePBX (tak, obsługuje .ael, ale nie wszystkie wersje i nie zawsze jest to wygodne)
Zamiast przetwarzania zakończenia przez exten=>h wprowadzono przetwarzanie przez hangup_handler, ponieważ dialplan FreePBX działał tylko z nim
Poprawiono linię wywołania skryptu, dodano cudzysłowy oraz zewnętrzny numer połączenia ExtNum
Przetwarzanie przeniesiono do kontekstów _custom, co pozwala nie zmieniać i nie edytować konfiguracji FreePBX — przychodzące przez [ext-did-custom], wychodzące przez [outbound-allroutes-custom]
Nie ma powiązania z numerami — plik jest uniwersalny i wymaga jedynie konfiguracji ścieżki oraz linku do serwera
Aby rozpocząć pracę, należy jeszcze puścić skrypty w AMI z użyciem loginu i hasła — do tego również w FreePBX znajduje się plik _custom
Plik manager_custom.conf
;; to login
[callmeplus]
;; to hasło
secret = trampampamturlala
deny = 0.0.0.0/0.0.0.0
;; pracuję z lokalną maszyną - ale jeśli trzeba, można wpisać inne
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,originateOba te pliki należy umieścić w /etc/asterisk, a następnie przeczytać konfiguracje (lub zrestartować Asteriska)
# astrisk -rv
Connected to Asterisk 16.6.2 currently running on freepbx (pid = 31629)
#freepbx*CLI> dialplan reload
Dialplan reloaded.
#freepbx*CLI> exitTeraz przejdźmy do PHP
Inicjalizacja skryptów i tworzenie usługi
Ponieważ schemat pracy z Bitrix 24, usługą dla AMI, nie jest całkowicie prosty i przejrzysty, trzeba się na nim zatrzymać osobno. Asterisk przy aktywacji AMI po prostu otwiera port i to wszystko. Przy dołączeniu klienta prosi o autoryzację, a następnie klient subskrybuje wymagane zdarzenia. Zdarzenia przychodzą w prostym tekście, który PAMI przekształca w zorganizowane obiekty i zapewnia możliwość ustawienia funkcji filtrowania tylko na interesujące zdarzenia, pola, numery itd.
Gdy tylko połączenie przychodzi, wyzwalane jest zdarzenie NewExten zaczynając od kontekstu macierzystego [from-pstn], a następnie następują wszystkie zdarzenia według kolejności linii w kontekstach. Po otrzymaniu informacji z zadanych w dialplanie _custom zmiennych CallMeCallerIDName oraz CallStart wywoływane jest
Funkcja żądania UserID, odpowiadającego wewnętrznemu numerowi, na który przychodzi połączenie. A jeśli to jest grupa połączeń? To kwestia polityczna, trzeba stworzyć połączenie dla wszystkich jednocześnie (gdy dzwonią wszyscy naraz) lub tworzyć połączenia w kolejności w miarę dzwonienia? Większość klientów ma strategię First Available, więc nie ma z tym problemów, dzwoni tylko jeden. Ale trzeba to rozwiązać.
Funkcja rejestracji połączenia w Bitrix24, która zwraca CallID, potrzebny później do raportowania parametrów połączenia i linku do nagrania. Wymaga albo wewnętrznego numeru, albo UserID.

Po zakończeniu połączenia wywoływana jest funkcja ładowania nagrania, która jednocześnie informuje o statusie zakończenia połączenia (Zajęty, Brak odpowiedzi, Sukces), a także ładuje link do pliku mp3 z nagraniem (jeśli dostępne).
Ponieważ moduł CallMeIn.php musi działać nieprzerwanie, utworzono dla niego plik uruchamiania SystemD. callme.service, który należy umieścić w /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
#trzeba zobaczyć, jakie uprawnienia mają foldery
#User=www-data #Ubuntu - debian
#User=nginx #Centos
[Install]
WantedBy=multi-user.targetInicjalizacja i uruchomienie skryptu odbywa się przez systemctl lub service.
# systemctl enable callme
# systemctl start callmeUsługa sama się uruchomi ponownie w razie potrzeby (przy awariach). Usługa monitorowania połączeń przychodzących nie wymaga instalacji serwera WWW, wystarczy php (który z pewnością jest na serwerze FreePBX). Ale w przypadku braku dostępu do nagrań rozmów przez serwer WWW (jeszcze z https) nie będzie możliwości odsłuchiwania nagrań rozmów.
Teraz porozmawiajmy o połączeniach wychodzących. Skrypt CallMeOut.php ma dwie funkcje:
Inicjowanie połączenia po złożeniu żądania do skryptu php (w tym za pomocą przycisku „Zadzwoń” w samym Bitrixie). Bez serwera WWW to nie działa, żądanie przychodzi przez HTTP POST, a w żądaniu zawarty jest token.
Informowanie o połączeniu, jego parametrach i nagraniach w Bitrix. Dzieje się to z inicjatywy Asteriska w planie połączeń [sub-call-internal-ended] po zakończeniu połączenia.

Serwer WWW potrzebny jest tylko do dwóch rzeczy — ładowania plików nagrań przez Bitrix (przez HTTPS) oraz wywołania skryptu CallMeOut.php. Można użyć wbudowanego serwera FreePBX, którego pliki znajdują się w /var/www/html, można też zainstalować inny serwer lub wpisać inną ścieżkę.
Serwer WWW
Zostawimy konfigurację serwera WWW do samodzielnego przestudiowania (, , ). Jeśli nie masz domeny, możesz spróbować FreeDomain( ), która za darmo przydzieli Ci nazwę dla Twojego białego IP (pamiętaj, aby przekierować porty 80, 443 przez router, jeśli zewnętrzny adres jest tylko na nim). Jeśli właśnie utworzyłeś domenę DNS, musisz poczekać (od 15 minut do 48 godzin), aż wszystkie serwery załadują się. Z doświadczenia pracy z krajowymi dostawcami — od 1 godziny do doby.
Automatyzacja instalacji
Na GitHubie rozpoczęto rozwój instalatora, aby można było instalować jeszcze łatwiej. Ale na papierze wszystko wyglądało gładko — na razie instalujemy to ręcznie, na szczęście po grzebaniu w tym wszystkim stało się całkowicie jasne, z kim się przyjaźni, kto gdzie chodzi i jak to debugować. Instalatora jeszcze nie ma (
Docker
Jeśli chcesz szybko przetestować rozwiązanie — jest opcja z Dockerem — szybko stwórz kontener, otwórz mu porty na zewnątrz, wstaw pliki konfiguracyjne i spróbuj (to opcja z kontenerem LetsEncrypt, jeśli certyfikat już masz, wystarczy przekierować odwrotny proxy na serwer WWW FreePBX (przydzieliliśmy mu inny port — 88), LetsEncrypt w dockerze zgodnie z pomysłem
Plik należy uruchomić w pobranej folderze projektu (po git clone), ale najpierw trzeba zajrzeć do konfiguracji Asteriska (folder asterisk) i wpisać tam ścieżki do zapisów i URL Twojej strony
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 # do konfiguracji
- 5060:5060\/udp
- 5160:5160\/udp
- 127.0.0.1:5038:5038 # dla 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:
Ten plik docker-compose.yaml uruchamia się przez
docker-compose up -d
Jeśli nginx się nie uruchomił, to coś jest nie tak z konfiguracją w folderze nginx\/ssl_docker.conf
Inne integracje
A czemu nie dodać kilka CRM do skryptów, pomyśleliśmy. Przeanalizowaliśmy kilka API innych CRM, w szczególności darmową zintegrowaną w niektórych ATS — ShugarCRM i Vtiger, i tak! można, zasada ta sama. Ale to już inna historia, którą później osobno wrzucimy na GitHub.
Linki
Sam kod na GitHubie —
Artykuł źródłowy do inspiracji
Zastrzeżenie: wszelkie zbieżności z rzeczywistością są przypadkowe i to nie byłem ja,
Źródło: habr.com
