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