#nullable disable
using CallFlow;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.Generic;
using TCX.Configuration;
using TCX.PBXAPI;
namespace dummy
{
//this handler plays holiday prompt as specified for destination
public class special_rg_call : ScriptBase<special_rg_call>
{
public string rgnumber = "800"; // ring group number as destination
public string extnumber = "220"; // extension number as ring group member which status decides about different routing
public string extstatus = "Away"; // extension status which decides about different routing, maybe Out Of Office, Custom 1 oder Custom 2
// nearly the reference implementation of trunk routing by 3CX, difference: match sequence of cid, did and standard route match
DestinationStruct FindRealDefaultDestination(ExternalLine trunk, string callerID, string DID)
{
DestinationStruct retval = new();
string[] range;
foreach (var a in trunk.RoutingRules)
{
bool match =
(a.Conditions.Condition.Type == RuleConditionType.BasedOnCallerID && // CID check
a.Data.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Any(x =>
x == "*" ||
x == callerID ||
x.StartsWith('*') && callerID.EndsWith(x[1..]) ||
x.EndsWith('*') && callerID.StartsWith(x[..^1]) ||
x.StartsWith('*') && x.EndsWith('*') && callerID.Contains(x[1..^1]) ||
((range = x.Split("-", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)).Length == 2 ?
(range[0].Length == callerID.Length && range[1].Length == callerID.Length) && (range[0].CompareTo(callerID) <= 0 && range[1].CompareTo(callerID) >= 0) : false)
)
)
||
(a.Conditions.Condition.Type == RuleConditionType.BasedOnDID && // DID check
(
a.Data == DID
|| (a.Data.StartsWith('*') && DID.EndsWith(a.Data[1..]))
))
||
(a.Conditions.Condition.Type == RuleConditionType.ForwardAll); // standard route check
if (match)
{
retval.CopyFrom(a.ForwardDestinations.OfficeHoursDestination);
break;
}
}
return retval;
}
public string CheckRG(DN dest)
{
string retval = null;
RingGroup rgdn = (MyCall.PS as PhoneSystem).GetDNByNumber(rgnumber) as RingGroup;
// check destination (number, if ringgroup) and status of all member
if (dest.Number.Equals(rgnumber) &&
rgdn != null &&
rgdn.GetType().ToString().Equals("TCX.Configuration.Interop.Wringgroup") &&
rgdn.Members.Where(m => m.Number != extnumber).All(m => (m as Extension).CurrentProfile.Name == "Out of office") &&
rgdn.Members.Any(m => m.Number == extnumber && (m as Extension).CurrentProfile.Name == extstatus))
{
// check business and non-business hours and destinations
var tbr = rgdn.GetTimeBasedRoutingInfo();
if (tbr.reason == CallControlAPI.DivertReason.OfficeHours ||
(tbr.reason == CallControlAPI.DivertReason.OutOfOfficeHours && rgdn.OutOfOfficeHoursRoute.To == DestinationType.ProceedWithNoExceptions) ||
(tbr.reason == CallControlAPI.DivertReason.BreakTime && rgdn.BreakTimeRoute.To == DestinationType.ProceedWithNoExceptions) ||
(tbr.reason == CallControlAPI.DivertReason.Holiday && rgdn.HolidaysRoute.To == DestinationType.ProceedWithNoExceptions) )
{
retval = extnumber; // yes, all conditions right, route to other number ...
}
}
return retval;
}
public override async Task<bool> StartAsync()
{
if (MyCall.Caller.DN is ExternalLine externalLine && MyCall.IsInbound) // calls only from trunk
{
// get the real inbound target
var defaultDestination = FindRealDefaultDestination(externalLine, MyCall.Caller.CallerID, MyCall.Caller.CalledNumber).Internal;
if (defaultDestination != null)
{
string destnr = CheckRG(defaultDestination);
if( destnr != null )
{
try
{
// route call to vmb: usually prepend "*4" to the number, get it from the global 3CX dial code parameter
destnr = (MyCall.PS as PhoneSystem).GetParameterByName("VMAIL").Value + destnr;
await MyCall.RouteTo(destnr, MyCall.Caller.CallerID, 3600);
return true; // a new route for incoming call, exit the script and further processing if transfer is done
}
catch (Exception ex)
{
MyCall.Error($"special_rg_call.cs script execution failed: {ex}");
}
}
}
}
return false; // return true means: call ends here, return false means: call will further processed by 3CX
}
}
}