Kombinieren von Anrufskripts

mae

Silver Partner
Basic Certified
Mitglied seit
24. Oktober 2024
Beiträge
3
Wir haben einen Kunden bei dem wir für jeden Feiertag unterschiedliche Ansagen haben möchten und ausserdem sollte jeden Donnerstag nach den Öffnungszeiten eine andere Ansage abgespielt werden als die restlichen Tage der Woche. Die jeweiligen Punkte können mit den Skripts aus dem Store gelöst werden aber besteht die Möglichkeit diese beiden Skripts in einem zusammen zu führen und wenn ja wie? Ich habe leider keine Kenntnis über C#.
 
bei dem wir für jeden Feiertag unterschiedliche Ansagen haben möchten
Dafür ist das Skript aus dem Store so wie hier beschrieben gut. Achtung: das funktioniert nur mit Bindung des SIP Trunk an eine Abteilung. Alternativ kann man auch das Skript hier benutzen, bitte lesen.

und ausserdem sollte jeden Donnerstag nach den Öffnungszeiten eine andere Ansage abgespielt werden als die restlichen Tage der Woche.
Das hat erst einmal nichts mit dem Feiertagskript zu tun. So einen Zeitraum kann man auch nicht als Feiertag festlegen. Wenn du schreibst:
Die jeweiligen Punkte können mit den Skripts aus dem Store gelöst werden
Wie würde es denn für diesen Zeitraum an jeden Donnerstag gelöst werden?

Wie ist es denn bisher mit den Ansagen nach Feierabend gelöst? Wie ist da der Call Flow in der 3CX, damit irgend etwas abgespielt wird wenn man was auch immer anruft? Beschreibe das bitte.
 
Wie würde es denn für diesen Zeitraum an jeden Donnerstag gelöst werden?
Mit dem timebasecallscript.cs kann der Anruf jeden Donnerstag auf ein IVR mit der gewünschten Ansage geleitet werden. Alle anderen Tage wird der Anruf auf den normalen Anrufverlauf der PBX verlaufen. (Wenn ich die Beschreibung des Skripts korrekt verstanden habe.)

Wie ist es denn bisher mit den Ansagen nach Feierabend gelöst? Wie ist da der Call Flow in der 3CX, damit irgend etwas abgespielt wird wenn man was auch immer anruft? Beschreibe das bitte.
Die Geschäftszeiten/Feiertage sind auf der Abteilung definiert. Ein Trunk und eine RG sind dieser Abteilung zugewiesen. Der Trunk bringt alle Anrufe auf die RG bei der ist definiert, Ausserhalb der GZ Ansage wiedergeben und Anruf beenden. Das selbe gilt für Feiertage.

Hoffe die Beschreibung reicht so aus.
 
Mit dem timebasecallscript.cs kann der Anruf jeden Donnerstag auf ein IVR mit der gewünschten Ansage geleitet werden. Alle anderen Tage wird der Anruf auf den normalen Anrufverlauf der PBX verlaufen. (Wenn ich die Beschreibung des Skripts korrekt verstanden habe.)
Aaaah, alles klar, danke für die Erklärung, das habe ich glatt unterschlagen.

Ja klar, man kann Skripte miteinander kombinieren und wird es in diesem Fall - Eingang eines Anrufes auf einem SIP Trunk und Start eines Anrufskriptes - auch tun müssen, wenn beide Skripte benötigt werden.

Der Trunk bringt alle Anrufe auf die RG ... Das selbe gilt für Feiertage.
Ja, auch wenn eine Rufgruppe diese Einstellung kennt und man diese so nutzen könnte - es wäre immer dieselbe Ansage für alle Feiertage. Auch wenn das akzeptabel wäre, das timebasecallscript.cs würde nicht reichen, weil es an an Feiertagen, in deinem Fall Donnerstags usw., anläuft und somit die Feiertagansage zu diesen Zeiten noch vorher übergeht.


Wenn dir die originalen 3CX Anrufskripte reichen, dann ist hier eine (von mir ungetestete) bei dir hoffentlich funktionierende Lösung:
C#:
#nullable disable
// https://www.3cx.com/docs/holiday-script/
using CallFlow;
using System;
using System.Threading;
using System.Threading.Tasks;
using TCX.Configuration;
using TCX.PBXAPI;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CallFlow.CFD;

namespace dummy
{
    public class HolidayAndTimebasedRouting : ScriptBase<HolidayAndTimebasedRouting>
    {
    //the calls during holidays which have either
    //OfficeHoliday.HolidayPrompt OR
    //if the file with <holiday name>.wav in the folder          Data/Ivr/Prompts/holidays is found,
    //will be redirected to the number specified here.
    //if prompt will not be found, the call will not intercepted
 
        const string destinationDN = "1002"; //routing destination if holiday is true

        // The destination DN to which the timebasecallscript call will be redirected
        const string DestinationDN = "8001";
   
        // Define a schedule for when calls should be intercepted
        static readonly Schedule schedule = new Schedule(RuleHoursType.SpecificHours)
        {
            { DayOfWeek.Monday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Monday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Tuesday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Tuesday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Wednesday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Wednesday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Thursday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Thursday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Friday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Friday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Saturday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Saturday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) },
            { DayOfWeek.Sunday, new Schedule.PeriodOfDay(TimeSpan.FromHours(17.5), TimeSpan.FromHours(24)) },
            { DayOfWeek.Sunday, new Schedule.PeriodOfDay(TimeSpan.FromHours(0), TimeSpan.FromHours(7)) }
        };    
     
        public override async Task<bool> StartAsync()
        {
            try
            {
                PhoneSystem ps = (PhoneSystem)MyCall.PS;
                if(MyCall.Caller.DN is ExternalLine externalLine)
                {
                    var trunkTimeBasedroute = MyCall.Caller.DN.GetTimeBasedRoutingInfo();
                    if(trunkTimeBasedroute.reason == CallControlAPI.DivertReason.Holiday && trunkTimeBasedroute.holiday!=null)
                    {
                        MyCall.Info($"the holiday is {trunkTimeBasedroute.holiday}");
                        var theFile = trunkTimeBasedroute.holiday.HolidayPrompt;
                        if(string.IsNullOrWhiteSpace(theFile))
                        {
                            theFile = Path.Combine(ps.GetParameterValue("IVRPROMPTPATH"), "holidays", trunkTimeBasedroute.holiday.Name+".wav");
                            MyCall.Info($"Check holidays prompt storage for {theFile}");
                            if(!File.Exists(theFile))
                            {
                                MyCall.Info($"No holiday prompt found. Skip holiday routing");
                                theFile = null;
                            }
                        }
                        else
                        {
                            MyCall.Info($"HolidayPrompt is {theFile}");
                        }
               
                        if(!string.IsNullOrWhiteSpace(theFile))
                        {
                            var holidayDestination =new DestinationStruct(ps.GetDNByNumber(destinationDN));
                       
                            MyCall.Info($"Playing prompt {theFile} and route call to {holidayDestination}");
                            await MyCall.AssureMedia()
                            .ContinueWith(x => MyCall.PlayPrompt(null, [theFile], PlayPromptOptions.Blocked))
                            .Unwrap()
                            .ContinueWith(x=>MyCall.RouteToAsync(holidayDestination))
                            .Unwrap();
                            return true;
                        }
                    }
                }
            }
            catch(Exception ex)
            {
                MyCall.Info($"Failed to intercept holiday route\n{ex}");
            }
            try     // original timebasecallscript.cs starts here
            {
                await Task.Run(async () =>
                {
                    try
                    {
                        MyCall.Debug($"Script start delay: {DateTime.UtcNow - MyCall.LastChangeStatus}");
                        MyCall.Debug($"Incoming connection {MyCall} from {MyCall.Caller}");
                        bool intercepted = false;
                        var ps = MyCall.PS as PhoneSystem;

                        if (MyCall.Caller.DN is ExternalLine externalLine)
                        {
                            var DIDNumber = MyCall.Caller["inbound_did"];
                            var CallerID = MyCall.Caller.CallerID;
                            var currentTime = externalLine.Now(out var utc, out var timezone, out var groupmode);

                            // Intercept calls during the specified schedule
                            if (schedule.IsActiveTime(currentTime))
                            {
                                string[] DIDs = { "*" }; // Allow all DIDs
                                string[] Callers = { "*" }; // Allow all Callers
                                var destination_struct = new DestinationStruct(ps.GetDNByNumber(DestinationDN));

                                // Check if the call's DID and CallerID match the interception criteria
                                if ((DIDs.Contains(DIDNumber) || DIDs.Contains("*")) &&
                                    (Callers.Contains(CallerID) || Callers.Contains("*")))
                                {
                                    try
                                    {
                                        var result = await MyCall.RouteToAsync(destination_struct);
                                        MyCall.Info($"{CallerID} -> {DIDNumber} has been redirected to {DestinationDN} ({result})");
                                        intercepted = true;
                                    }
                                    catch (Exception ex)
                                    {
                                        MyCall.Info($"{CallerID} -> {DIDNumber}: interception failed. '{DestinationDN}' is not reachable: {ex}");
                                    }
                                }
                                else
                                {
                                    MyCall.Info($"{CallerID} -> {DIDNumber}@{currentTime}: Default CallerID/DID based routing will be applied");
                                }
                            }
                        }

                        MyCall.Return(intercepted);
                    }
                    catch (Exception ex)
                    {
                        MyCall.Error($"Script execution failed: {ex}");
                        MyCall.Return(false);
                    }
                });
            }
            catch (Exception ex)
            {
                MyCall.Error($"Task execution failed: {ex}");
                MyCall.Return(false);
            }

            return false;
        }
    }
}

Passe dir das an, probiere das selber aus und beschreibe uns hier bitte das Ergebnis.
 
Zuletzt bearbeitet:
Manchmal denkt man nicht an die einfachen Sachen, insbes. wenn man tagsüber zu viel parallel und nicht in einer 3CX macht. Das o.g. ist alles viel zu kompliziert, das muss gar nicht sien.

Die Geschäftszeiten/Feiertage sind auf der Abteilung definiert. Ein Trunk und eine RG sind dieser Abteilung zugewiesen. Der Trunk bringt alle Anrufe auf die RG
Wenn das nur wenige oder wie bei dir nur eine DID für eine solche gewünschte Zeitschaltung sein soll, dann geht das einfacher und wie bisher mit einer Dummy NSt. und eigenen GZ auf dieser. Das holiday Skript kommt wenn gewünscht im Original zum Einsatz und danach geht der Ruf nicht an die RG sondern einen Dummy Nutzer mit eigenen GZ (Donnerstags ...). Auch ohne Anmeldung eines Endgerätes beim Dummy Nutzer schaltet dieser seinen Status und damit auf ihn eingehende Anrufe um, z.B. eben auf die RG oder eine Ansage per IVR.

Die Vorteile: es werden mehr Standardelemente einer 3CX genutzt, es ist i.d.R. ersichtlicher, die GZ und die PZ können bequem in der 3CX Administration per Klickibunti eingestellt und spontan per Umschaltung der Dummy NSt. getestet oder gar festgelegt werden (evtl. das auch wieder mit einer CFD App).

Das haben wir schon immer so benutzt. So eine Dummy NSt. mit eigenen GZ ist ein Schalter 1aus3. Das kann man auch kaskadieren.

Jetzt kommts:
Mit einer v20 geht es nun auch anders: nutze keinen Dummy Benutzer sondern eine Abteilung für die Donnerstagsumschaltzeiten. Die Abteilung bekommt die Zeiten eingestellt, bekommt die DID zugeordnet und in deren Einstellungen erfolgt die selektive Weiterleitung auf die RG oder den IVR oder auch gleich eine Ansage ohne IVR (weil der IVR durchwahlfähig ist und so umgangen werden kann).
 
Zuletzt bearbeitet:

Statistik des Forums

Themen
44.413
Beiträge
232.713
Mitglieder
78.328
Neuestes Mitglied
as7h