Ich habe keine CS Entwickler Umgebung und die Suchfunktion in der Dokumentation geht nicht.Die Beschreibung zu TimeBasedRoutingInfo fehlt halt überall und ich wurstel mich da so durch deren Eigenschaften durch, vielleicht fehlt da einfach etwas.
Grundsätzlich könnte man das Skript aber auch mit den bisherigen Mitteln der Abfrage der Feiertage, deren Zuordnung zu Abteilungen und der dafür hinterlegten Audio Dateien anders bauen. Vor allem funktionstüchtig. Das ist kein Hexenwerk. Da fehlt mir persönlich aber grad etwas die Zeit.

Wenn man z.B., wie vorgeschlagen, MyCall.RouteTarget.GetFullSnapshot() folgt (ist ein DN) bzw. MyCall.RouteTarget.GetFullSnapshot().Number (deren lesbare Nummer), dann bekommt man die Nummer des SIP Trunk. Dieser hat eben ab der v20 keine Zeiten und keine Feiertage mehr. Daher bleibt die TimeBasedDestination struct (Doku: CallFlow -> Structures -> TimeBasedDestination) leer. Ich suche noch die Funktion, welche mir eine DN bzw. Nummer nach dem erfolglosen Routing (return false) des Anrufverarbeitungsskript zurückgibt, auf Grund dessen ich auf eine Abteilung schließen kann von der ich dann wiederum die Feiertage lesen kann. Ich suche noch ...
GetDNByNumber() gibt die DN einer Entität auf Grund einer Nummer, xyz.Number die Nummer sofern das geht. Aber das kennst du wohl schon. Geht es dir sonst darum, die SIP Trunks alle zu finden?Wie weisst du, dass man damit die DN.Number des Trunks erhält?
Ich im Prinzip auch. Ich lasse die Ausgabe mittels z.B.Ich weiss leider noch immer nicht wie ich das Zeugs "debuggen" kann, habe bis jetzt aber auch nur direkt mit den Scripts im WebGui gearbeitet.
string sLogFile = @"C:/ProgramData/3CX/Instance1/Data/Logs/3CXCallFlowFX.log";
...
try { File.AppendAllText(sLogFile, "\n##### CFA C# Log: time|"+ DateTime.Now.ToString("H:mm:ss") +"|\n");} catch(Exception ex) { MyCall.Info($"Failed CFA\n{ex}"); }
Diese Zuordnung stimmt bei uns praktisch nirgends. Wenn überhaupt, dann evtl. nur kurz nach einer Ersteinrichtung. Wir haben das nirgends fest und halten das auch nicht aufrecht. Sonst wären z.B. div. WS und DR mitten im Nummernblock der Nutzer.... damit könntest du die MyCall.DialedNumber auf die Anzahl interne Stellen kürzen und so die DN erhalten, sofern die korrespondieren.
Ich meine, weil du gesagt hast:GetDNByNumber() gibt die DN einer Entität auf Grund einer Nummer, xyz.Number die Nummer sofern das geht. Aber das kennst du wohl schon. Geht es dir sonst darum, die SIP Trunks alle zu finden?
Ich kann mit MyCall.RouteTarget.GetFullSnapshot() der Vergleich ziehen, ob das Ziel (also die erhaltene DN) ein User (interface Extension) ist oder nicht, weshalb es mich verwundert, dass du mit MyCall.RouteTarget.GetFullSnapshot().Number die Nummer des Trunks erhälst.Wenn man z.B., wie vorgeschlagen, MyCall.RouteTarget.GetFullSnapshot() folgt (ist ein DN) bzw. MyCall.RouteTarget.GetFullSnapshot().Number (deren lesbare Nummer), dann bekommt man die Nummer des SIP Trunk.
Werde ich mal so probieren, da komme ich sicher weiter als bis anhin! Danke dir vielmals!Ich im Prinzip auch. Ich lasse die Ausgabe mittels z.B.
in eine separate Datei schreiben und monitore diese. Sehr primitiv, gebe ich zu, aber reicht teilweise.C#:string sLogFile = @"C:/ProgramData/3CX/Instance1/Data/Logs/3CXCallFlowFX.log"; ... try { File.AppendAllText(sLogFile, "\n##### CFA C# Log: time|"+ DateTime.Now.ToString("H:mm:ss") +"|\n");} catch(Exception ex) { MyCall.Info($"Failed CFA\n{ex}"); }
Das liegt wohl daran, weil bis zu diesem Zeitpunkt alle Daten den SIP Trunk als akt. Endpunkt halten, noch nicht einmal das Anrufverarbeitungsskript (was zu dem Zeitpunkt transparent 'nebenher' gestartet wurde und daher kein Ziel ist). Siehe Beschreibung RouteTarget, DNRef, GetFullSnapshot: gives fresh copy of .... Das ist leider keine Glaskugel für folgende Routing Punkte.Ich kann mit MyCall.RouteTarget.GetFullSnapshot() der Vergleich ziehen, ob das Ziel (also die erhaltene DN) ein User (interface Extension) ist oder nicht, weshalb es mich verwundert, dass du mit MyCall.RouteTarget.GetFullSnapshot().Number die Nummer des Trunks erhälst.


Nice.3CX v20 u3 Alpha, abrufbares Feiertag Skript:
Anhang anzeigen 16038
Aktuell verweist dieser Link auf denselben Blog Eintrag / Skript wie zuvor. Also kein anderes Skript im Moment. Aber man findet es direkt.Das scheint jetzt ein anderes Skript zu sein.
Ja, das geht - je nachdem wie man das sieht.Unterschiedliche Feiertage auf verschiedenen Nummern vom selben Trunk scheinen immer noch nicht zugehen, oder hat da mittlerweile jemand was herausgefunden?
Ich kenne mich nicht gut mit C# Code aus, habe nur bemerkt das wenn man den Namen des Skripts anklickt (nicht den Link) wird eindeutlich kürzeres Skript als das vom Blog Post eingefügt. Deshalb dachte ich es hat sich evtl. was verändert.Aktuell verweist dieser Link auf denselben Blog Eintrag / Skript wie zuvor. Also kein anderes Skript im Moment. Aber man findet es direkt.
Ja das ist genau das was ich meinte, schade.Aber: wird ein SIP Trunk mit einigen DID für verschiedene Abteilungen (mit anderen GZ usw.) genutzt, so kann man das Skript nicht verwenden. Das fehlt.
Das ist derselbe Link und dahinter auch immer noch dasselbe Skript wie bei der ersten Veröffentlichung vom 21.06.24.Ich kenne mich nicht gut mit C# Code aus, habe nur bemerkt das wenn man den Namen des Skripts anklickt (nicht den Link) wird eindeutlich kürzeres Skript als das vom Blog Post eingefügt. Deshalb dachte ich es hat sich evtl. was verändert.
Ich meine ja eben nicht den Link. Sondern wenn man hier drauf drückt:Das ist derselbe Link und dahinter auch immer noch dasselbe Skript wie bei der ersten Veröffentlichung vom 21.06.24.

Der Punkt 2 aus dem Blog-Post ist gemäss meinem Test jetzt nicht mehr nötig. Weiss zwar nicht ob das bereits vorher geändert hat.
- Ensure the file name and the holiday name are an exact match.
/*
* Play Holiday Prompts - Use code with caution!
* Requires holidays and prompts to be configured on department office hours
* Requires trunk to be part of the department and holidays configured at department level
* Based on the entered PIN, the call is routed to a predefined destination.
* Script intercepts inbound calls on a trunk and checks if it is a holiday in Office hours - Department - Office holidays
* If the day is set as a holiday and a prompt is configured, the prompt will be played and call wil be routed to the holiday routing destination
* If there is no prompt, the prompt will be skipped
*/
#nullable disable
using CallFlow;
using CallFlow.CFD;
using TCX.Configuration;
using System.Threading.Tasks;
using TCX.PBXAPI;
namespace dummy
{
public class PlayHolidayPromptBeforeRouting : ScriptBase<PlayHolidayPromptBeforeRouting>
{
string GetRealPromptFileName(OfficeHoliday holiday)
{
return holiday?.HolidayPrompt;
}
public override async Task<bool> StartAsync()
{
PhoneSystem ps = (PhoneSystem)MyCall.PS;
if (MyCall.Caller.DN is ExternalLine externalLine)
{
var trunkTimeBasedroute = MyCall.Caller.DN.GetTimeBasedRoutingInfo();
if (
trunkTimeBasedroute.reason == CallControlAPI.DivertReason.Holiday
&& GetRealPromptFileName(trunkTimeBasedroute.holiday) is string thePath
&& !string.IsNullOrWhiteSpace(thePath)
)
{
await MyCall.AssureMedia()
.ContinueWith(x => MyCall.PlayPrompt(null, new[] { thePath }, PlayPromptOptions.Blocked))
.Unwrap();
}
}
return false;
}
}
}
#nullable disable
using CallFlow;
using System;
using System.Threading;
using System.Threading.Tasks;
using TCX.Configuration;
using TCX.PBXAPI;
using System.Collections.Generic;
using System.IO;
namespace dummy
{
public class PlayHolidayPromptBeforeRouting : ScriptBase<PlayHolidayPromptBeforeRouting>
{
//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
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}");
}
return false;
}
}
}
Das habe ich noch gar nicht probiertSondern wenn man hier drauf drückt:
2005 gegründet, als VoIP noch eine aufstrebende Technologie war, hat sich 3CX seither zu einem globalen Marktführer im Bereich der Geschäftskommunikation entwickelt.