Jak utworzyć skrypt przetwarzania połączeń
- Wstęp
- Wywoływanie skryptu przetwarzania połączeń
- Przegląd API przetwarzania połączeń
- Metoda grupy Task
RouteToAsync(this ActiveConnection ac, ) - Metoda grupy Task
DivertAsync(this ActiveConnection ac, ) - Metoda grupy Task
ReplaceWithAsync(this ActiveConnection ac, ) - Przykładowy skrypt przetwarzania połączeń
- Jak uruchomić skrypt
- Komentarze do kodu
- Przykładowy kod
- Zobacz również
Wstęp
Skrypty przetwarzania połączeń to potężna nowa funkcja wersji V20. Pozwalają one przechwytywać połączenia i przetwarzać je za pomocą standardowego kodu C# – dając praktycznie nieograniczone możliwości analizy i zastosowania niestandardowej logiki. Oto kilka przykładów zastosowania:
- Analiza identyfikatora dzwoniącego (Caller ID) i przypisanie połączenia do określonych agentów.
- Wyszukiwanie klientów na podstawie identyfikatora dzwoniącego i odpowiednie kierowanie połączeń.
- Sprawdzanie godziny i daty w celu odpowiedniego przetworzenia połączenia.
- Odtwarzanie określonej zapowiedzi w zależności od daty.
Wywoływanie skryptu przetwarzania połączeń
Zanim napiszesz skrypt, musisz przemyśleć, gdzie i w jaki sposób ma być on uruchamiany. Możesz skonfigurować dla niego kod wybierania, przypisać mu numer DID lub przekierować połączenia do skryptu na podstawie określonej zmiennej.
W aktualizacji 2 będzie można wyzwalać skrypt dla każdego przychodzącego połączenia na SIP Trunk!
Po określeniu sposobu wywoływania skryptu należy przefiltrować połączenia i napisać logikę przetwarzania połączeń.
Przegląd API przetwarzania połączeń
API składa się z trzech podstawowych metod służących do przekazywania połączeń do nowej destynacji.
Metoda grupy Task<CallControlResult> RouteToAsync(this ActiveConnection ac,<Destination>)
Ta metoda tworzy trasę powiązaną z określonym połączeniem ac.
- ac – aktywne połączenie należące do RoutePoint. Gdy nowa destynacja odbierze połączenie, jej połączenie zastępuje ac (uczestnictwo RoutePoint).
- Skrypt powinien obsługiwać błędy zadania. Może po prostu wywołać MyCall.Return, aby zakończyć własne połączenie z dzwoniącym.
- Skrypt może tworzyć dowolną liczbę tras, ale pierwsza odebrana trasa anuluje wszystkie pozostałe i zastąpi uczestnictwo RoutePoint w połączeniu (co zakończy połączenie dla RoutePoint).
- RouteToAsync może być wykonane w dowolnym stanie połączenia RoutePoint, co pozwala na wykonywanie operacji w tle podczas komunikacji z dzwoniącym (np. odtwarzanie komunikatów, obsługa DTMF itp.).
- Gdy zadanie powiedzie się, połączenie jest usuwane z RoutePoint (MyCall zostaje rozłączone), a nowa destynacja przejmuje obsługę połączenia.
Metoda grupy Task<CallControlResult> DivertAsync(this ActiveConnection ac,<Destination>)
Ta metoda przekierowuje połączenie do nowej destynacji bez ustanawiania (odbierania) połączenia (RoutePoint nie dzwoni).
- Jeśli połączenie zostało już ustanowione z RoutePoint, metoda zakończy się niepowodzeniem – należy użyć RouteToAsync lub ReplaceWithAsync.
- Aktywne połączenie (w stanie dzwonienia) należące do RoutePoint zostanie zastąpione nową destynacją, a RoutePoint zostanie odłączony od połączenia.
- Metoda ta jest przydatna, jeśli RoutePoint nie musi wchodzić w interakcję z dzwoniącym.
- Jeśli zadanie zakończy się niepowodzeniem, skrypt może nadal obsługiwać połączenie z dzwoniącym.
- Jeśli zadanie zakończy się powodzeniem, połączenie zostaje usunięte z RoutePoint (MyCall zostaje zakończone), a nowa destynacja przejmuje obsługę połączenia. Skrypt RoutePoint przechodzi w tryb zakończenia (Wrap Up) i musi dokończyć swoje zadanie.
Metoda grupy Task<CallControlResult> ReplaceWithAsync(this ActiveConnection ac,<Destination>)
Znana również jako metoda „ślepego transferu” (blind transfer).
- Dozwolona tylko w trybie połączonym (RoutePoint zaakceptował połączenie i komunikuje się z użytkownikiem).
- Dzwoniący zostanie umieszczony w stanie zawieszenia (on hold).
- Zadanie zakończy się niepowodzeniem, jeśli destynacja będzie niedostępna.
- Skrypt może kontynuować przetwarzanie połączenia po niepowodzeniu zadania (jeśli dzwoniący nadal jest połączony z RoutePoint).
- Jeśli zadanie zakończy się powodzeniem, połączenie skryptu (ICallHandler.MyCall) zostanie zakończone, a skrypt odłączony od obsługi połączenia – nowy uczestnik przejmie obsługę dzwoniącego.
Przykładowy skrypt przetwarzania połączeń
Ten przykład demonstruje sposób tworzenia niestandardowego RoutePoint oraz jego programowania:
- Podstawową strukturę kodu C# dostarczanego do RoutePoint.
- Podstawowe użycie metod rozszerzeń TCX.PBXAPI.CallControlAPI dla interfejsu ActiveConnection (ICall).
- Podstawowe operacje na obiekcie MyCall, dostarczanym przez CallFlowScriptingCore.
- Podstawowe operacje na konfiguracji (parametry PBX).
- Użycie metody rozszerzenia RouteToAsync dla obiektu ActiveConnection.
Przykład funkcjonalności RoutePoint:
- RoutePoint akceptuje wyłącznie połączenia przekazane za pomocą ślepego transferu z telefonu (numeru wewnętrznego). Połączenia bezpośrednie są odrzucane.
- Dowolna liczba połączeń może być jednocześnie przekazana do RoutePoint z dowolnego numeru wewnętrznego (każde połączenie jest obsługiwane oddzielnie).
- Połączenie jest zwracane bezpośrednio do przekazującego użytkownika (numeru wewnętrznego) po 15 sekundach (bez dodatkowego przekierowania). Jeśli zwrócone połączenie nie zostanie odebrane w ciągu 15 sekund, jest anulowane i ponownie inicjowane po kolejnych 15 sekundach.
- Osoba dzwoniąca słyszy muzykę na czekanie zgodnie z konfiguracją Parking w systemie PBX.
Jak uruchomić skrypt
- Utwórz RoutingPoint z dowolnym numerem – na przykład #101. Ustaw właściwość RoutePoint.ScriptCode na poniższy kod (interfejs użytkownika nadal nie pozwala na tworzenie punktów routingu z niestandardowym kodem – wymaga pliku ZIP, co nie jest konieczne w przypadku prostych skryptów).
- RoutePoint powinien pojawić się na odpowiedniej liście („CFD applications” w tym momencie) z zieloną kropką (kompilacja nie powinna zakończyć się błędem dla tego kodu).
- Następnie:
- Jeśli dowolny numer wewnętrzny przekieruje połączenie na numer #101, po 15 sekundach połączenie zostanie zwrócone.
- Dzwoniący usłyszy muzykę na czekanie, zgodnie z konfiguracją dla Parkingu. (Skrypt wykorzystuje to ustawienie, ale kod można zmodyfikować, aby generował inne treści dla dzwoniącego).
- Jeśli zwrócone połączenie nie zostanie odebrane, RoutePoint ponowi próbę co 15 sekund po poprzedniej próbie, aż dzwoniący zakończy połączenie lub pierwotny przekierowujący je odbierze (lub zostanie ono przechwycone).
Komentarze do kodu
- Kod obiektu „scripted” opiera się na (wykorzystuje, implementuje i/lub dziedziczy):
- CallFlow namespace
- CallFlow.ICall
- CallFlow.ICallHandler
- CallFlow.ICallHandlerEx
- CallFlow.ScriptBase<T>
- Klasa „Script object” musi dziedziczyć CallFlow.ScriptBase<T> i implementować wszystkie wymagane metody abstrakcyjne dla swojej instancji.
- Obiekt jest uruchamiany, gdy ScriptingHost wykonuje handler połączenia za pomocą metody ICallHandler.Start.
- Skrypt musi kończyć się wywołaniem ICall.Return w sposób jawny.
- Preferowaną implementacją metody ICall.Start jest „async void” , która uruchamia oddzielne zadanie (należy obsłużyć wszystkie wyjątki).
- Implementacja skryptu powinna kontrolować wyłącznie obiekt MyCall, udostępniony przez Scripting Host. Jest to jedyny obiekt związany z sesją skryptu połączenia
- Gdy MyCall (czyli udział punktu RoutePoint w połączeniu) zostanie zakończony, implementacja skryptu powinna zakończyć sesję i zamknąć skrypt.
- Skrypt przepływu połączeń nie służy do monitorowania konfiguracji systemu ani żadnych zewnętrznych zasobów.
- Nie jest również narzędziem do śledzenia wszystkich połączeń w systemie.
- Jest to wyłącznie logika RoutePoint, która może być zintegrowana z innymi przepływami połączeń.
- Innymi słowy: skrypt obsługuje jedno z połączeń, które są związane z RoutePoint, ale nigdy nie inicjuje nowego połączenia.
- Routing API jest enkapsulowane w statycznej klasie TCX.PBXAPI.CallControlAPI, która udostępnia metody rozszerzające dla:
- TCX.Configuration.ActiveCannection
- TCX.Configuration.DN
- TCX.Configuration.RegistrarRecord
Przykładowy kod
#nullable disable using CallFlow; using System; using System.Threading; using System.Threading.Tasks; using TCX.Configuration; using TCX.PBXAPI; namespace dummy { public class ParkingRoutePointSample : ScriptBase<ParkingRoutePointSample> { async Task<CallControlResult> ProcessAutoPickup(RoutePoint sp, DestinationStruct returnTo, CancellationToken token) { while (true) try { return await Task.Delay(TimeSpan.FromSeconds(15), token).ContinueWith(x => { MyCall.Trace("{0} - automatic redirection of the call from {1}.{2} to '{3}'", MyCall.DN, MyCall.Caller?.CallerID, MyCall.Caller?.DN, returnTo); return MyCall.RouteToAsync(new RouteRequest { RouteTarget = returnTo, TimeOut = TimeSpan.FromSeconds(15) //will ring until failure } ); } , TaskContinuationOptions.NotOnCanceled).Unwrap(); } catch (OperationFailed ex) { MyCall.Trace("Automatic redirection failed: {0}", ex.TheResult); MyCall.Trace("Continue hold call from {0}({1}) on {2}", MyCall.Caller?.CallerID, MyCall.Caller?.DN, MyCall.DN); continue; } } PhoneSystem ps = null; /// <summary> /// /// </summary> public override async void Start() { await Task.Run(async () => { try { MyCall.Debug($"Script start delay: {DateTime.UtcNow - MyCall.LastChangeStatus}"); MyCall.Debug($"Incoming connection {MyCall}"); ps = MyCall.PS as PhoneSystem; CallControlResult lastresult = null; DN referredBy = null; RoutePoint thisPark = null; string callerID = ""; DN callerDN = null; bool scriptCompleted = true; try { referredBy = MyCall.ReferredByDN?.GetFullSnapshot() as Extension; thisPark = MyCall.DN?.Clone() as RoutePoint; callerID = MyCall.Caller?.CallerID; callerDN = MyCall.Caller?.DN?.Clone() as DN; MyCall.Trace( "Parked call from {0}({1}) on {2}", callerID, callerDN, thisPark ); if (referredBy == null) { MyCall.Trace("{0} rejects call from {1}. Reason: No referrer specified", thisPark, callerDN); return; } var cancelationToken = new CancellationTokenSource(); MyCall.OnTerminated += () => { cancelationToken.Cancel(); }; lastresult = await MyCall.AssureMedia().ContinueWith( x => { if(!string.IsNullOrWhiteSpace(ps.GetParameterValue("PARK_MOH_SOURCE"))) MyCall.SetBackgroundAudio(true, new string[] { ps.GetParameterValue("PARK_MOH_SOURCE") }); else MyCall.SetBackgroundAudio(true, new string[] { ps.GetParameterValue("MUSICONHOLDFILE") }); return ProcessAutoPickup(thisPark, new DestinationStruct(referredBy), cancelationToken.Token); }, TaskContinuationOptions.OnlyOnRanToCompletion).Unwrap(); } catch (PBXIsNotConnected ex) { MyCall.Error($"Call control API is not available:\n{ex}"); scriptCompleted = false; } catch (TaskCanceledException) { MyCall.Trace($"Call was disconnected from parking place"); } catch (Exception ex) { MyCall.Error($"Parking failure:\n{ex}"); scriptCompleted = false; } finally { try { MyCall.Info("Call from {0}({1}) parked by {2} on {3} finished with result={4}", callerID, callerDN, referredBy, thisPark, lastresult?.ToString() ?? "terminated"); } catch (Exception ex) { MyCall.Error($"SharedParkingFlow finalize exception {ex}"); } MyCall.Return(scriptCompleted); } } catch { MyCall.Return(false); } }); } } }
Zobacz również
- API kontroli połączeń dla Linuxa
- API kontroli połączeń dla Windowsa
- Google Cloud Storage i Speech API
- Skrypt przetwarzania połączeń dla wejścia DTMF
Ostatnia aktualizacja
Ten dokument został ostatnio zaktualizowany 5 marca 2024
https://www.3cx.pl/docs/administrator/skrypt-przetwarzania-polaczen/
