Aller au contenu

Script de routage des appels

De INDYWiki

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é
  1. Nommer le script
  2. Choisir Quand cette SDA est composée
  3. Choisir la SDA appelée qui va déclencher le script
  4. Choisir le scope d'action du script
  5. Cliquer sur OK
  6. Coller le script préparé dans la zone script
  7. Sauvegarder
Valide v20