Erstellen eines Skripts zur Anrufverarbeitung

Einführung

Skripte zur Verarbeitung von Anrufen sind eine leistungsstarke neue Funktion von 3CX V20. Sie ermöglichen es Ihnen, Anrufe zu erfassen und sie mit Standard-C#-Code zu verarbeiten – was Ihnen im Wesentlichen unbegrenzte Möglichkeiten bietet, einen Anruf zu analysieren und benutzerdefinierte Logik anzuwenden. Hier ein paar Beispiele:

  • Analysieren Sie die Rufnummer/Caller-ID und weisen Sie bestimmte Agenten zu.
  • Führen Sie Kundendatensuchen basierend auf der Rufnummer durch und leiten Sie sie entsprechend weiter.
  • Lassen Sie Anrufe unter Berücksichtigung von Datum und Uhrzeit verarbeiten.
  • Lassen Sie Ansagen unter Berücksichtigung des Datum wiedergeben.

Auslösen eines Skripts zur Anrufverarbeitung

Bevor Sie ein Skript erstellen, müssen Sie festgelegt haben, wann und wie es ausgelöst werden soll. Sie können einen Funktionscode für das Skript erstellen, ihm eine DID-Nummer zuweisen, damit Anrufe direkt zugewiesen werden, oder Anrufe unter Berücksichtigung einer Variablen an das Skript weiterleiten lassen.

In 3CX V20 Update 2 wird es zudem möglich sein, bei jedem für einen SIP-Trunk eingehenden Anruf ein Skript auszulösen.

Wenn Sie festgelegt haben, wie das Skript ausgelöst werden soll, müssen Sie Anrufe filtern und die Verarbeitungslogik definieren.

Übersicht über die Anrufverarbeitungs-API

Die API besteht aus drei Kernmethoden, die Anrufe an das neue Ziel weiterleiten.

Methodengruppe Task<CallControlResult> RouteToAsync(this ActiveConnection ac,<Destination>)

Diese Methode erstellt eine Route, die an die angegebene Verbindung ac gebunden ist

  • ac – die aktive Verbindung im Besitz von RoutePoint. Wenn das neue Ziel antwortet, ersetzt seine Verbindung den ac. (Beteiligung von RoutePoint).
  • Das Skript sollte Aufgabenfehler behandeln. Das Skript kann einfach MyCall.Return aufrufen, um seine eigene Verbindung mit dem Anrufer zu beenden.
  • Das Skript kann versuchen, so viele Routen zu erstellen, wie es benötigt, aber die erste beantwortete Route löscht alle anderen und ersetzt die Teilnahme von RoutePoint am Anruf (beendet den Anruf für RoutePoint).
  • RouteToAsync kann in jedem Zustand der RoutePoint-Verbindung ausgeführt werden. Daher kann der RoutePoint bei der Kommunikation mit dem Anrufer eine Weiterleitung im Hintergrund durchführen (Ansagen abspielen, DTMFs verarbeiten usw.).
  • Wenn die Aufgabe erfolgreich ist, wird der Anruf von RoutePoint zurückgezogen (MyCall wird getrennt) und das neue Ziel führt weiterhin die Verarbeitung aus.

Methodengruppe Task<CallControlResult> DivertAsync(this ActiveConnection ac,<Destination>)

Diese Methode leitet den Anruf an das neue Ziel um, ohne dass eine Verbindung aufgebaut (beantwortet) wird (Klingeln am RoutePoint).

  • Wenn bereits ein Anruf mit RoutePoint hergestellt wurde, schlägt die Methode fehl und RouteToAsyc/ReplaceWithAsync sollte verwendet werden
  • Die aktive Verbindung (im Signalisierungszustand) von RoutePoint wird durch ein neues Ziel ersetzt und RoutePoint wird vom Anruf getrennt.
  • Diese Methode ist nützlich, wenn der Routenpunkt nicht mit dem Anrufer interagieren muss.
  • Wenn eine Aufgabe fehlgeschlagen ist, verarbeitet das Skript möglicherweise weiterhin die Verbindung mit dem Aufrufer.
  • Wenn die Aufgabe erfolgreich ist, wird der Anruf von RoutePoint zurückgezogen (MyCall-Verbindung wird beendet) und das neue Ziel beginnt mit der Anrufverarbeitung. Das RoutePoint-Skript wechselt in den Nachbearbeitungsmodus und muss seine eigene Aufgabe abschließen.

Methodengruppe Task<CallControlResult> ReplaceWithAsync(this ActiveConnection ac,<Destination>)

Auch als ”Blind Transfer“-Methode bekannt.

  • Nur im verbundenen Modus zulässig (RoutePoint hat den Anruf angenommen und interagiert mit dem verwendeten).
  • Der Anrufer wird in die Warteschleife gelegt.
  • Die Aufgabe schlägt fehl, wenn das Ziel nicht erreichbar ist.
  • Das Skript kann den Anruf nach einem Aufgabenfehler weiter verarbeiten (sofern der Anrufer weiterhin mit RoutePoint verbunden ist).
  • Wenn eine Aufgabe erfolgreich ist, wird die Skriptverbindung (ICallHandler.MyCall) beendet und das Skript von der Anrufverarbeitung getrennt (der neue Teilnehmer kümmert sich um den Anrufer).

Beispiel für ein Skripts zur Anrufverarbeitung

Dieses Beispiel zeigt, wie Sie einen benutzerdefinierten Routenpunkt erstellen und programmieren:

  1. Grundstruktur des an RoutePoint bereitgestellten C#-Codes
  2. Grundlegende Verwendung der TCX.PBXAPI.CallControlAPI-Erweiterungsmethoden für die ActiveConnection (ICall)-Schnittstelle
  3. Grundlegendes Arbeiten mit dem von CallFlowScriptingCore bereitgestellten MyCall-Objekt
  4. Grundlegendes Arbeiten mit der Konfiguration (PBX-Parameter)
  5. Verwendung der RouteToAsyc-Erweiterungsmethode des ActiveConnection-Objekts

Ein Beispiel für die RoutePoint-Funktionalität:

  • Der Routenpunkt akzeptiert nur Anrufe, die per Blindweiterleitung vom Telefon (Nebenstelle) gesendet werden. Direktanrufe werden abgewiesen.
  • Unbegrenzte Anrufe können von jeder Nebenstelle gleichzeitig an diesen RoutePoint weitergeleitet werden (jeder Anruf wird separat bearbeitet).
  • Der Anruf wird nach 15 Sekunden direkt an den Übermittler (Nebenstelle) zurückgesendet (keine Weiterleitung angewendet). Wenn der Rückruf nicht innerhalb von 15 Sekunden beantwortet wird, wird der Anruf abgebrochen und nach 15 Sekunden erneut wiederholt.
  • Der Anrufer hört Wartemusik, da das Parken auf der PBX konfiguriert ist.

Ausführen des Skripts

  • Erstellen Sie einen RoutingPoint mit einer beliebigen Zahl – zum Beispiel Nr. 101 – und setzen Sie die RoutePoint.ScriptCode-Eigenschaft auf den folgenden Text (die Benutzeroberfläche lässt immer noch nicht zu, dass ein Routing-Punkt mit benutzerdefiniertem (handgeschriebenem Code) erstellt wird. Es ist eine Art ZIP-Datei erforderlich, die für ein einfaches Skript eigentlich nicht benötigt wird)
  • RoutePoint sollte in der entsprechenden Liste (“CFD-Anwendungen” derzeit) mit einem grünen Punkt erscheinen [die Kompilierung sollte für diesen Code nicht fehlschlagen])
  • Dann:
  • Wenn eine Nebenstelle ihren Anruf an die Nummer #101 weiterleitet, wird der Anruf innerhalb von 15 Sekunden zurückgesendet.
  • Der Anrufer hört Wartemusik, die für das Parken konfiguriert ist. (Das Skript verwendet diese Einstellung, der Code kann jedoch geändert werden, um andere Inhalte für den Anrufer zu generieren.)
  • Wenn der Rückruf nicht beantwortet wird, versucht der RoutePoint es 15 Sekunden nach dem vorherigen Versuch erneut (und noch einmal), solange der Anrufer den Anruf nicht abbricht oder der ursprüngliche Übermittler antwortet (oder sein Anruf angenommen wird).

Kommentare zum Code

  • Der Code des “skriptgesteuerten” Objekts basiert auf (verwendet, implementiert und/oder erbt)
  • CallFlow namespace
  • CallFlow.ICall
  • CallFlow.ICallHandler
  • CallFlow.ICallHandlerEx
  • CallFlow.ScriptBase<T>
  • Die Klasse “Skriptobjekt” muss CallFlow.ScriptBase<T> erben und alle für ihre Instanz erforderlichen abstrakten Methoden implementieren.
  • Das Objekt wird ausgeführt, wenn der ScriptingHost den Aufrufhandler mithilfe der ICallHandler.Start-Methode ausführt.
  • Das Skript muss explizit mit ICall.Return beendet werden.
  • Die bevorzugte Implementierung der ICall.Start-Methode ist ”async void”, mit der eine getrennte Aufgabe ausgeführt wird (alle Ausnahmen müssen abgefangen werden).
  • Die Skriptimplementierung darf nur das von Scripting Host verfügbar gemachte MyCall-Objekt steuern. Es ist das einzige Element der Anrufskriptsitzung.
  • Wenn MyCall (Teilnahme des Routenpunkts am Anruf) beendet ist, sollte die Skriptimplementierung ihre Sitzung abschließen und beenden.
  • Das Anrufablaufskript ist keine Möglichkeit, die Systemkonfiguration oder externe Ressourcen zu überwachen.
  • Es ist keine Möglichkeit, alle Anrufe im System zu überwachen.
  • Es handelt sich lediglich um eine RoutePoint-Logik, die in andere Anrufabläufe integriert werden kann.
  • Mit anderen Worten: Das Skript verarbeitet einen der Anrufe, die mit RoutePoint verbunden sind, initiiert jedoch niemals einen neuen Anruf.
  • Die Routing-API ist in der statischen Klasse TCX.PBXAPI.CallControlAPI gekapselt, die folgende Nebenstellenmethoden verfügbar macht
  • TCX.Configuration.ActiveCannection
  • TCX.Configuration.DN
  • TCX.Configuration.RegistrarRecord

Beispielcode

#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);

                }

            });

        }

    }

}

Siehe auch

Letztes Update

Dieses Dokument wurde zuletzt am 11. März  2024 aktualisiert.

https://www.3cx.de/docs/adminhandbuch/anrufverarbeitungsskripte/