ProHoster > Blog > yönetim > FreePBX'i anlama ve onu Bitrix24 ve daha fazlasıyla entegre etme
FreePBX'i anlama ve onu Bitrix24 ve daha fazlasıyla entegre etme
Bitrix24 CRM, iş akışı, muhasebe ve yöneticilerin gerçekten sevdiği ve BT personelinin pek sevmediği diğer birçok şeyi birleştiren devasa bir kombinasyondur. Portal, küçük klinikler, üreticiler ve hatta güzellik salonları da dahil olmak üzere birçok küçük ve orta ölçekli şirket tarafından kullanılmaktadır. Yöneticilerin "sevdiği" ana işlev, telefon ve CRM'nin entegrasyonudur, herhangi bir arama anında CRM'ye kaydedildiğinde, müşteri kartları oluşturulur, geldiğinde müşteri hakkında bilgiler görüntülenir ve onun kim olduğunu, ne olduğunu hemen görebilirsiniz. satabilir ve ne kadar borcu var. Ancak Bitrix24'ten telefon hizmeti ve bunun CRM ile entegrasyonu maliyetlidir, bazen çok yüksek. Makalede size açık araçlar ve popüler IP PBX ile entegrasyon deneyimini anlatacağım. ÜcretsizPBXve ayrıca çeşitli parçaların çalışma mantığını da göz önünde bulundurun
IP telefon satan ve yapılandıran, entegre eden bir şirkette dış kaynak olarak çalışıyorum. Bitrix24'ü müşterilerin sahip olduğu PBX'lerle ve ayrıca çeşitli VDS şirketlerindeki sanal PBX'lerle entegre etmek için buna ve bu şirkete bir şey teklif edip edemeyeceğimiz sorulduğunda, Google'a gittim. Ve tabii ki bana bir bağlantı verdi habr'daki makale, burada bir açıklama ve github var ve her şey çalışıyor gibi görünüyor. Ancak bu çözümü kullanmaya çalışırken, Bitrix24'ün artık eskisi gibi olmadığı ve birçok şeyin yeniden yapılması gerektiği ortaya çıktı. Ek olarak, FreePBX sizin için çıplak bir yıldız işareti değildir, burada yapılandırma dosyalarında kullanım kolaylığı ile güçlü bir arama planını nasıl birleştireceğinizi düşünmeniz gerekir.
İşin mantığını inceliyoruz
Yeni başlayanlar için, her şey nasıl çalışmalı? PBX dışından bir arama alındığında (sağlayıcıdan gelen SIP INVITE olayı), arama planının (arama planı, arama planı) işlenmesi başlar - aramanın ne ve hangi sırada yapılacağına ilişkin kurallar. İlk paketten, daha sonra kurallarda kullanılabilecek birçok bilgi alabilirsiniz. SIP'nin içini incelemek için mükemmel bir araç, analizördür. sngrep (bağlantıapt install/yum install ve benzerleri aracılığıyla popüler dağıtımlara kolayca yüklenir, ancak kaynaktan da oluşturulabilir. Sngrep'teki arama günlüğüne bakalım
Basitleştirilmiş bir biçimde, arama planı yalnızca ilk paketle, bazen konuşma sırasında da, aramaların aktarılmasıyla, düğme basmalarıyla (DTMF), FollowMe, RingGroup, IVR ve diğerleri gibi çeşitli ilginç şeylerle ilgilenir.
Davet Paketinin içinde neler var?
Aslında, en basit arama planları ilk iki alanla çalışır ve tüm mantık DID ve Arayan Kimliği etrafında döner. DID - aradığımız yer, Arayan Kimliği - kim arıyor.
Ama sonuçta, bir şirketimiz var ve tek bir telefonumuz yok - bu, PBX'in büyük olasılıkla şehir numaralarında (Çalma Grubu), IVR'de (Merhaba, siz aradınız ...) arama gruplarına (birkaç cihazın eşzamanlı / ardışık çalması) sahip olduğu anlamına gelir. biri ...), Telesekreterler ( Cümleler), Zaman Koşulları, Başka numaralara veya bir hücreye Yönlendirme (Beni Takip Et, Yönlendir). Bu, bir çağrı geldiğinde gerçekte kimin aranacağını ve kiminle konuşacağını kesin olarak belirlemenin çok zor olduğu anlamına gelir. İşte müşterilerimizin PBX'inde tipik bir aramanın başlangıcına bir örnek
Arama başarılı bir şekilde PBX'e girdikten sonra, farklı "bağlamlarda" arama planında dolaşır. Asterisk açısından bağlam, her biri aranan numaraya göre bir filtre içeren numaralandırılmış bir komut dizisidir (exten=DID başlangıç aşamasında harici bir arama için exten olarak adlandırılır). Arama planı satırındaki komutlar herhangi bir şey olabilir - dahili işlevler (örneğin, dahili bir aboneyi arayın - Dial(), telefonu kapat - Hangup()), koşullu işleçler (IF, ELSE, ExecIF ve benzeri), bu bağlamın diğer kurallarına geçişler (Goto, GotoIF), bir işlev çağrısı (Gosub, Macro) biçimindeki diğer bağlamlara geçiş. Ayrı bir direktif include имя_контекста, geçerli bağlamın sonuna başka bir bağlamdan komutlar ekleyen. Dahil etme yoluyla dahil edilen komutlar her zaman yürütülür sonra Geçerli bağlamın komutları.
FreePBX'in tüm mantığı, Gosub, Macro ve Handler işleyicileri aracılığıyla dahil etme ve çağırma yoluyla farklı bağlamların birbirine dahil edilmesi üzerine kuruludur. Gelen FreePBX çağrılarının bağlamını göz önünde bulundurun
Çağrı yukarıdan aşağıya sırayla tüm bağlamlardan geçer, her bağlamda makrolar (Makro), işlevler (Gosub) veya sadece geçişler (Goto) gibi diğer bağlamlara çağrılar olabilir, bu nedenle çağrılan şeyin gerçek ağacı yalnızca günlüklerde izlenebilir.
Tipik bir PBX için tipik bir kurulum şeması aşağıda gösterilmiştir. Arama yaparken, gelen rotalarda DID aranır, bunun için geçici koşullar kontrol edilir, her şey yolundaysa ses menüsü açılır. Ondan, düğme 1'e veya zaman aşımına basarak, arama operatörleri grubuna çıkın. Arama sona erdikten sonra, arama planı makrosu çağrılır ve bundan sonra arama planında özel işleyiciler (kapatma işleyici) dışında hiçbir şey yapılamaz.
Bu çağrı algoritmasının neresinde CRM'e çağrının başladığını, kaydın nereden başlayacağını, kaydın nerede biteceğini ve CRM'ye çağrı bilgisi ile birlikte gönderilmesini sağlamalıyız?
Harici sistemlerle entegrasyon
PBX ve CRM entegrasyonu nedir? Bu iki platform arasında veri ve olayları çevirerek birbirine gönderen ayarlar ve programlardır. Bağımsız sistemlerin iletişim kurmasının en yaygın yolu API'ler aracılığıyladır ve API'lere erişmenin en popüler yolu HTTP REST'tir. Ama yıldız işareti için değil.
Yıldız işaretinin içinde:
AGI - esas olarak arama planında kullanılan harici programların/bileşenlerin senkronize çağrılması, aşağıdakiler gibi kütüphaneler vardır: phpagi, PAGİ
AMI - olaylara abone olma ve metin komutları girme ilkesiyle çalışan, içeriden SMTP'ye benzeyen, olayları izleyebilen ve aramaları yönetebilen bir metin TCP soketi, bir kütüphane var PAMİ - Asterisk ile bağlantı oluşturmak için en popüler
AMI çıktı örneği
Etkinlik: Yeni kanal
Ayrıcalık: çağrı, tümü
Kanal: PJSIP/VMS_pjsip-0000078b
Kanal Durumu: 4
ChannelStateDesc: Halka
Arayan Kimliği: 111222
Arayan Kimliği Adı: 111222
BağlıHatNum:
bağlı hat adı:
Dil: tr
hesap kodu:
Bağlam: pstn'den
Uzatma: s
Öncelik: 1
Benzersiz kimlik: 1599589046.5244
Bağlantı kimliği: 1599589046.5244
ARI, JSON formatında REST, WebSocket aracılığıyla her ikisinin bir karışımıdır - ancak yeni kitaplıklar ve sarmalayıcılarla, çok iyi değil, önceden bulundu (phparia, Phpari) yaklaşık 3 yıl önce gelişmelerinde oldu.
Uygunluk veya rahatsızlık, belirli bir API ile çalışmanın olasılığı veya imkansızlığı, çözülmesi gereken görevlerle belirlenir. CRM ile entegrasyon için yapılması gerekenler şu şekildedir:
Aramanın başlangıcını, nereye aktarıldığını izleyin, CallerID, DID, başlangıç ve bitiş zamanlarını, belki de dizinden verileri çıkarın (telefon ile CRM kullanıcısı arasında bir bağlantı aramak için)
Görüşme kaydını başlat ve bitir, istenilen formatta kaydet, kayıt sonunda dosyanın nerede olduğunu bildir
Harici bir olayda (programdan) bir arama başlatın, dahili bir numarayı, harici bir numarayı arayın ve onları bağlayın
İsteğe bağlı: yer olmadığında aramaların otomatik olarak aktarılması için CRM, çevirici grupları ve FollowME ile entegre edin (CRM'ye göre)
Tüm bu görevler AMI veya ARI aracılığıyla çözülebilir, ancak ARI çok daha az bilgi sağlar, çok fazla olay yoktur, AMI'nin hala sahip olduğu birçok değişken (örneğin, makro çağrıları, makroların içindeki ayar değişkenleri, çağrı kaydı dahil) izlenmez. Bu nedenle, doğru ve doğru izleme için şimdilik (tamamen değil) AMI'yi seçelim. Ek olarak (bu olmadan nerede olurdu, biz tembel insanlarız) - orijinal çalışmada (habr'daki makale) PAMI'yi kullanın. *O zaman ARI'ye yeniden yazmaya çalışmanız gerekir, ancak işe yarayacağı gerçeğini değil.
Entegrasyonu yeniden keşfetmek
FreePBX'imizin aramanın başlangıcı, bitiş zamanı, numaraları, kayıtlı dosyaların adları hakkında AMI'ye basit yollarla rapor verebilmesi için, orijinal yazarlarla aynı numarayı kullanarak aramanın süresini hesaplamak en kolay yoldur. - değişkenlerinizi girin ve çıktıyı varlıkları için ayrıştırın. PAMI, bunu basitçe bir filtre işlevi aracılığıyla yapmayı önerir.
İşte aramanın başlangıç zamanı için kendi değişkeninizi ayarlamanın bir örneği (s, arama planında DID aramasına başlamadan ÖNCE gerçekleştirilen özel bir sayıdır)
FreePBX, extension.conf ve extension_ dosyalarının üzerine yazdığı içinek.conf, dosyayı kullanacağız uzantı_görenek.conf
extension_custom.conf'un tam kodu
[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
Orijinal makalenin yazarlarının orijinal arama planından özellik ve fark -
FreePBX'in istediği gibi .conf formatında arama planı (evet, .ael olabilir, ancak tüm sürümler değil ve her zaman uygun değildir)
Sonu exten=>h aracılığıyla işlemek yerine, FreePBX arama planı yalnızca onunla çalıştığı için işleme hangup_handler aracılığıyla başlatıldı
Sabit kod arama dizesi, eklenen alıntılar ve harici arama numarası ExtNum
İşleme, _özel bağlamlara taşınır ve [ aracılığıyla gelen FreePBX yapılandırmalarına dokunmanıza veya bunları düzenlemenize izin vermezext-did-özel], [ üzerinden gidengiden-allroutes-özel]
Sayılara bağlanma yok - dosya evrenseldir ve yalnızca sunucuya giden yol ve bağlantı için yapılandırılması gerekir
Başlamak için ayrıca AMI'de kullanıcı adı ve parola ile komut dosyaları çalıştırmanız gerekir - bunun için FreePBX'in ayrıca bir _custom dosyası vardır
manager_custom.conf dosyası
;; это логин
[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
Bu dosyaların her ikisi de /etc/asterisk dizinine yerleştirilmeli, ardından yapılandırmaları yeniden okumalı (veya yıldızı yeniden başlatmalı)
# astrisk -rv
Connected to Asterisk 16.6.2 currently running on freepbx (pid = 31629)
#freepbx*CLI> dialplan reload
Dialplan reloaded.
#freepbx*CLI> exit
Şimdi PHP'ye geçelim.
Komut dosyalarını başlatma ve bir hizmet oluşturma
AMI için bir hizmet olan Bitrix 24 ile çalışma şeması tamamen basit ve şeffaf olmadığından, ayrıca tartışılmalıdır. Yıldız işareti, AMI etkinleştirildiğinde, yalnızca bağlantı noktasını açar ve bu kadar. Bir müşteri katıldığında yetkilendirme ister, ardından müşteri gerekli olaylara abone olur. Olaylar, PAMI'nin yapılandırılmış nesnelere dönüştürdüğü ve filtreleme işlevini yalnızca ilgili olaylar, alanlar, sayılar vb. için ayarlama yeteneği sağlayan düz metin olarak gelir.
Çağrı gelir gelmez, üst [from-pstn] bağlamından başlayarak NewExten olayı başlatılır, ardından tüm olaylar bağlamlardaki satır sırasına göre gider. _özel arama planında belirtilen CallMeCallerIDName ve CallStart değişkenlerinden bilgi alındığında,
Aramanın geldiği dahili numaraya karşılık gelen UserID'yi isteme işlevi. Ya bu bir çevirmeli ağ grubuysa? Soru politik, herkese aynı anda (herkes aynı anda aradığında) bir çağrı oluşturmanız mı yoksa sırayla aradığınızda onların aradıkları gibi mi oluşturmanız gerekiyor? Çoğu müşteri İlk Müsait stratejisine sahiptir, dolayısıyla bunda bir sorun yoktur, yalnızca bir çağrı vardır. Ama sorunun çözülmesi gerekiyor.
Bitrix24'te arama parametrelerini ve kayda bir bağlantıyı bildirmek için gerekli olan Arama Kimliği'ni döndüren arama kaydı işlevi. Dahili numara veya Kullanıcı Kimliği gerektirir
Aramanın sona ermesinden sonra, aynı anda aramanın tamamlanma durumunu (Meşgul, Cevap yok, Başarılı) bildiren ve ayrıca (varsa) kayıtla birlikte mp3 dosyasına bir bağlantı indiren kayıt indirme işlevi çağrılır.
CallMeIn.php modülünün sürekli çalışması gerektiğinden, bunun için bir SystemD başlangıç dosyası oluşturulmuştur. beni ara.hizmet/etc/systemd/system/callme.service içine konması gereken
[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
komut dosyasının başlatılması ve başlatılması systemctl veya hizmet aracılığıyla gerçekleşir
Hizmet, gerektiğinde kendini yeniden başlatır (çökme durumunda). Gelen kutusu izleme hizmeti için web sunucusu kurulması gerekmez, sadece php gereklidir (ki bu kesinlikle FeePBX sunucusundadır). Ancak Web sunucusu üzerinden arama kayıtlarına erişim olmadığında (https ile de), arama kayıtlarının dinlenmesi mümkün olmayacaktır.
Şimdi giden aramalardan bahsedelim. CallMeOut.php betiğinin iki işlevi vardır:
Bir php betiği için bir istek alındığında bir çağrının başlatılması (Bitrix'in kendisindeki "Ara" düğmesinin kullanılması dahil). Web sunucusu olmadan çalışmaz, istek HTTP POST aracılığıyla alınır, istek bir belirteç içerir
Çağrı, parametreleri ve Bitrix'teki kayıtları hakkında mesaj. Bir arama sona erdiğinde [alt arama dahili uçlu] arama planında Yıldız işaretiyle tetiklendi
Web sunucusuna yalnızca iki şey için ihtiyaç duyulur - Bitrix kayıt dosyalarını indirmek (HTTPS yoluyla) ve CallMeOut.php komut dosyasını çağırmak. Dosyaları /var/www/html olan yerleşik FreePBX sunucusunu kullanabilir, başka bir sunucu kurabilir veya farklı bir yol belirleyebilirsiniz.
Web sunucusu
Web sunucusu kurulumunu bağımsız çalışmaya bırakalım (tyts, tyts, tyts). Bir alan adınız yoksa, FreeDomain'i deneyebilirsiniz( https://www.freenom.com/ru/index.html), beyaz IP'niz için size ücretsiz bir ad verecektir (harici adres yalnızca üzerindeyse, 80, 443 bağlantı noktalarını yönlendirici aracılığıyla iletmeyi unutmayın). Yeni bir DNS etki alanı oluşturduysanız, tüm sunucular yüklenene kadar (15 dakikadan 48 saate kadar) beklemeniz gerekir. Yerli sağlayıcılarla çalışma deneyimine göre - 1 saatten bir güne.
Kurulum otomasyonu
Kurulumu daha da kolaylaştırmak için github üzerinde bir yükleyici geliştirilmiştir. Ancak kağıt üzerinde sorunsuzdu - biz hepsini manuel olarak kurarken, tüm bunlarla uğraştıktan sonra neyin kiminle arkadaş olduğu, kimin nereye gittiği ve nasıl hata ayıklanacağı kristal netliğiyle ortaya çıktı. Henüz yükleyici yok
liman işçisi
Çözümü hızlı bir şekilde denemek istiyorsanız - Docker ile bir seçenek var - hızlı bir şekilde bir kap oluşturun, dışarıya bağlantı noktaları verin, ayar dosyalarını kaydırın ve deneyin (zaten bir sertifikanız varsa bu, LetsEncrypt kapsayıcısındaki seçenektir) , ters proxy'yi FreePBX web sunucusuna yönlendirmeniz yeterlidir (başka bir bağlantı noktası 88 olarak verdik), LetsEncrypt in docker tabanlı Bu makalede
Dosyayı indirilen proje klasöründe (git klonundan sonra) çalıştırmanız gerekir, ancak önce yıldız yapılandırmalarına (yıldız işareti klasörü) girip kayıtlara giden yolları ve sitenizin URL'sini buraya yazmanız gerekir.
Bu docker-compose.yaml dosyası şu yolla çalıştırılır:
docker-compose up -d
Nginx başlamazsa, nginx/ssl_docker.conf klasöründeki yapılandırmada bir sorun var demektir.
Diğer entegrasyonlar
Ve neden aynı anda komut dosyalarına biraz CRM koymayalım diye düşündük. Başta ücretsiz yerleşik PBX - ShugarCRM ve Vtiger olmak üzere birkaç başka CRM API'sini inceledik ve evet! evet prensip aynı Ancak bu, daha sonra ayrı ayrı github'a yükleyeceğimiz başka bir hikaye.