Script de routage des appels
Apparence
Permet en fonction du moment de la semaine de router les appels vers telle ou telle destination. Nécessite d'ouvrir le site H24 ( voir écran ci-dessous) sur le département / instance 3CX ( c'est le script qui gère )
⚠️ les règles sont évaluées dans l'ordre de leur apparition, il faut donc les déclarer de la plus spécifique à la plus générale ⚠️
Le script
Seules les lignes jaune requiert d'être éditées. Si plus de règles sont à prévoir, dupliquer les sections new RoutingRule.
- Ligne 16 : si aucune règle dans le script ne permet de déterminer un chemin, alors c'est cette extension la cible
- Ligne 44 : Pour chaque règle, il faut définir les jours et horaires. sur cet exemple, le lundi de 14 à 17h, c'est la règle 1 qui s'applique.
- Ligne 50 : C'est l'extension qui va être visée par la règle, peut être un utilisateur, un SVI, un groupe , une file d'attente.
- Ligne 51 : DID : ce filtre permet de tester le numéro appelé ( SDA actif sur le système). En pratique on ne s'en sert pas ici car le filtre se fait en amont de l'appel du script. Mais possible. La syntaxe * permet de tout laisser passer
- Ligne 52 : Callers, on peut filtrer sur le numéros de l'appelant. La syntaxe * permet de tout laisser passer
#nullable disable
using CallFlow;
using System;
using System.Threading.Tasks;
using TCX.Configuration;
using TCX.PBXAPI;
using System.Collections.Generic;
using System.Linq;
using CallFlow.CFD;
namespace interceptcall
{
public class InterceptInboundCall : ScriptBase<InterceptInboundCall>
{
// Destination par Défaut, à priori jamais utilisé car ce numéro doit toujours être joignable
const string DefaultDestinationDN = "812";
// Structure d'une règle de routage
class RoutingRule
{
public Schedule Schedule { get; set; }
public string DestinationDN { get; set; }
public string[] DIDs { get; set; }
public string[] Callers { get; set; }
public RoutingRule(Schedule schedule, string destinationDN, string[] dids, string[] callers)
{
Schedule = schedule;
DestinationDN = destinationDN;
DIDs = dids;
Callers = callers;
}
}
// Règle(s) de routage "First Match" : La première règle satisfaite est éxécutée, les suivantes sont ignorées
static readonly List<RoutingRule> routingRules = new List<RoutingRule>
{
// Règle 1 - Mairie ouverte - routage vers le 801 qui route vers le 100 ( pas de changements
new RoutingRule(
new Schedule(RuleHoursType.SpecificHours)
{
{ DayOfWeek.Monday, new Schedule.PeriodOfDay(new TimeSpan(14,00,0), new TimeSpan(17,00,0)) },
{ DayOfWeek.Tuesday, new Schedule.PeriodOfDay(new TimeSpan(14,00,0), new TimeSpan(17,00,0)) },
{ DayOfWeek.Wednesday, new Schedule.PeriodOfDay(new TimeSpan(09,00,0), new TimeSpan(12,00,0)) },
{ DayOfWeek.Thursday, new Schedule.PeriodOfDay(new TimeSpan(09,00,0), new TimeSpan(12,00,0)) },
{ DayOfWeek.Friday, new Schedule.PeriodOfDay(new TimeSpan(09,00,0), new TimeSpan(12,00,0)) }
},
"801",
new[] { "*" }, // Tous les DIDs
new[] { "*" } // Tous les callers
),
// Règle 2 — Mairie fermée en semaine ( routage vers le 800 )
new RoutingRule(
new Schedule(RuleHoursType.SpecificHours)
{
{ DayOfWeek.Monday, new Schedule.PeriodOfDay(new TimeSpan(8,00,0), new TimeSpan(14,00,0)) },
{ DayOfWeek.Monday, new Schedule.PeriodOfDay(new TimeSpan(17,00,0), new TimeSpan(24,00,0)) },
{ DayOfWeek.Tuesday, new Schedule.PeriodOfDay(new TimeSpan(0,00,0), new TimeSpan(14,00,0)) },
{ DayOfWeek.Tuesday, new Schedule.PeriodOfDay(new TimeSpan(17,00,0), new TimeSpan(24,00,0)) },
{ DayOfWeek.Wednesday, new Schedule.PeriodOfDay(new TimeSpan(0,00,0), new TimeSpan(09,00,0)) },
{ DayOfWeek.Wednesday, new Schedule.PeriodOfDay(new TimeSpan(12,00,0), new TimeSpan(24,00,0)) },
{ DayOfWeek.Thursday, new Schedule.PeriodOfDay(new TimeSpan(0,00,0), new TimeSpan(09,00,0)) },
{ DayOfWeek.Thursday, new Schedule.PeriodOfDay(new TimeSpan(12,00,0), new TimeSpan(24,00,0)) },
{ DayOfWeek.Friday, new Schedule.PeriodOfDay(new TimeSpan(0,00,0), new TimeSpan(09,00,0)) },
{ DayOfWeek.Friday, new Schedule.PeriodOfDay(new TimeSpan(12,00,0), new TimeSpan(24,00,0)) }
},
"800",
new[] { "*" },
new[] { "*" }
),
// Règle 3 — Mairie fermée le week-end ( vendredi 16h30 au lundi ? )
new RoutingRule(
new Schedule(RuleHoursType.SpecificHours)
{
{ DayOfWeek.Friday, new Schedule.PeriodOfDay(new TimeSpan(16,30,0), new TimeSpan(24,00,0)) },
{ DayOfWeek.Saturday, new Schedule.PeriodOfDay(new TimeSpan(0,00,0), new TimeSpan(24,00,0)) },
{ DayOfWeek.Sunday, new Schedule.PeriodOfDay(new TimeSpan(0,00,0), new TimeSpan(24,00,0)) }
{ DayOfWeek.Monday, new Schedule.PeriodOfDay(new TimeSpan(0,00,0), new TimeSpan(08,00,0)) }
},
"804",
new[] { "*" },
new[] { "*" }
),
};
// Appel de la fonction principale
public override async void Start()
{
try
{
await Task.Run(async () =>
{
bool intercepted = false;
var ps = MyCall.PS as PhoneSystem;
if (!(MyCall.Caller.DN is ExternalLine externalLine))
{
MyCall.Return(false);
return;
}
var DIDNumber = MyCall.Caller["inbound_did"] ?? "";
var CallerID = MyCall.Caller.CallerID ?? "";
var currentTime = externalLine.Now(out var utc, out var timezone, out var groupmode);
MyCall.Info($"Incoming call | Caller: {CallerID} | DID: {DIDNumber} | Time: {currentTime}");
foreach (var rule in routingRules)
{
if (!rule.Schedule.IsActiveTime(currentTime))
continue;
bool didMatch = rule.DIDs.Contains("*") || rule.DIDs.Contains(DIDNumber);
bool callerMatch = rule.Callers.Contains("*") || rule.Callers.Contains(CallerID);
if (!didMatch || !callerMatch)
continue;
var destinationDN = ps.GetDNByNumber(rule.DestinationDN);
if (destinationDN == null)
{
MyCall.Error($"Destination {rule.DestinationDN} not found.");
continue;
}
try
{
var result = await MyCall.RouteToAsync(new DestinationStruct(destinationDN));
MyCall.Info($"Routed to {rule.DestinationDN} | Result: {result}");
intercepted = true;
break; // Stop at first matching rule
}
catch (Exception ex)
{
MyCall.Error($"Routing failed to {rule.DestinationDN}: {ex}");
}
}
// Utilisation de la route par défaut
if (!intercepted)
{
var fallbackDN = ps.GetDNByNumber(DefaultDestinationDN);
if (fallbackDN != null)
{
try
{
var result = await MyCall.RouteToAsync(new DestinationStruct(fallbackDN));
MyCall.Info($"Fallback routing to {DefaultDestinationDN} | Result: {result}");
intercepted = true;
}
catch (Exception ex)
{
MyCall.Error($"Fallback routing failed: {ex}");
}
}
else
{
MyCall.Error($"Fallback DN {DefaultDestinationDN} not found.");
}
}
MyCall.Return(intercepted);
});
}
catch (Exception ex)
{
MyCall.Error($"Script execution failed: {ex}");
MyCall.Return(false);
}
}
}
}
Mise en place du script
Connecté en admin dans 3CX :
Admin / Intégrations / Scripts d'appels / + Ajouter personnalisé

- Nommer le script
- Choisir Quand cette SDA est composée
- Choisir la SDA appelée qui va déclencher le script
- Choisir le scope d'action du script
- Cliquer sur OK
- Coller le script préparé dans la zone script
- Sauvegarder
| v20 |
