- Registrato
- 28 Marzo 2024
- Messaggi
- 8
Blocco Chiamate Temporaneo per Riunioni - Soluzione Funzionante
Ciao a tutti! Dopo varie sperimentazioni sono riuscito a creare un sistema per bloccare temporaneamente le chiamate in ingresso durante le riunioni senza interferire con i messaggi fuori orario esistenti. Ve lo condivido perché so che molti hanno questa esigenza.Il Problema
Nel nostro ufficio avevamo bisogno di poter bloccare le chiamate durante briefing e riunioni importanti, ma senza perdere le configurazioni esistenti del centralino. Serviva qualcosa di semplice: componi un numero, si attiva il blocco con messaggio personalizzato, ricomponi un altro numero e torna tutto normale.La Soluzione che Funziona
Dopo vari tentativi con gli script di default che forzavano tutto il sistema fuori orario (e creavano casino), ho trovato questo approccio che funziona benissimo:- 9999 = Attiva blocco riunioni
- 9998 = Disattiva blocco riunioni
- Le chiamate bloccate sentono un messaggio tipo "Tutte le linee sono occupate per riunione interna, richiamate più tardi"
- Il vero fuori orario continua a funzionare normalmente
Come Implementarlo
Cosa Serve
- 3CX V20 (testato su Update 2+)
- Accesso amministrativo
- Un po' di pazienza per la configurazione iniziale
Passo 1: IVR per il Messaggio Riunioni
Andate in Gestione chiamate → Digital Receptionist e create:- Nome: "IVR Riunioni" (o come preferite)
- Numero interno: 83 (o uno libero)
- Dipartimento: DEFAULT
- Caricate il vostro file audio personalizzato
- Timeout: 3 secondi → "Riattacca"
Passo 2: Script di Intercettazione
Questo è il pezzo più importante. Andate in Integrazioni → Script di chiamata → Aggiungi dal negozio e scegliete timebasecallscript.cs.Configurazione:
- Nome: qualcosa tipo "intercetta_riunioni"
- Quando eseguire: "Alla ricezione di una chiamata su un trunk"
- Selezionate i vostri trunk principali
- System Wide
#nullable disable<br>using CallFlow;<br>using System;<br>using System.IO;<br>using System.Threading;<br>using System.Threading.Tasks;<br>using TCX.Configuration;<br>using TCX.PBXAPI;<br>using System.Collections.Generic;<br>using System.Linq;<br>using CallFlow.CFD;<br><br>namespace interceptcall<br>{<br> public class InterceptInboundCall : ScriptBase<InterceptInboundCall><br> {<br> const string DestinationDN = "83"; // Il vostro IVR riunioni<br> const string FlagPath = "/var/lib/3cxpbx/Instance1/Data/Logs/RIUNIONE_ATTIVA.flag";<br><br> public override async void Start()<br> {<br> try<br> {<br> await Task.Run(async () =><br> {<br> try<br> {<br> MyCall.Debug($"Chiamata in arrivo: {DateTime.UtcNow}");<br> bool intercepted = false;<br> var ps = MyCall.PS as PhoneSystem;<br><br> if (MyCall.Caller.DN is ExternalLine externalLine)<br> {<br> var DIDNumber = MyCall.Caller["inbound_did"];<br> var CallerID = MyCall.Caller.CallerID;<br><br> // Qui è la magia: controlliamo se esiste il file flag<br> if (File.Exists(FlagPath))<br> {<br> var destination_struct = new DestinationStruct(ps.GetDNByNumber(DestinationDN));<br><br> try<br> {<br> var result = await MyCall.RouteToAsync(destination_struct);<br> MyCall.Info($"Chiamata da {CallerID} reindirizzata per riunioni");<br> intercepted = true;<br> }<br> catch (Exception ex)<br> {<br> MyCall.Error($"Errore nel redirect: {ex}");<br> }<br> }<br> else<br> {<br> MyCall.Debug($"Nessuna riunione attiva, routing normale");<br> }<br> }<br><br> MyCall.Return(intercepted);<br> }<br> catch (Exception ex)<br> {<br> MyCall.Error($"Errore script: {ex}");<br> MyCall.Return(false);<br> }<br> });<br> }<br> catch (Exception ex)<br> {<br> MyCall.Error($"Errore critico: {ex}");<br> MyCall.Return(false);<br> }<br> }<br> }<br>}<br>
Passo 3: Script per Attivazione (9999)
Sempre in Integrazioni → Script di chiamata, aggiungi dal negozio extavailable.cs:- Nome: "attiva_riunioni"
- Codice di composizione: 9999
- System Wide
using System;<br>using System.IO;<br>using System.Threading.Tasks;<br>using TCX.Configuration;<br>using CallFlow;<br>using System.Linq;<br>using System.Collections.Generic;<br><br>namespace dummy<br>{<br> public class AttivaRiunioni : ScriptBase<AttivaRiunioni><br> {<br> public override async Task<bool> StartAsync()<br> {<br> const string FlagPath = "/var/lib/3cxpbx/Instance1/Data/Logs/RIUNIONE_ATTIVA.flag";<br> <br> if (MyCall.Caller.DN is Extension ext && MyCall.ReferredByDN is not DN)<br> {<br> try<br> {<br> await File.WriteAllTextAsync(FlagPath, DateTime.Now.ToString());<br> MyCall.Info($"Riunioni attivate da interno {ext.Number}");<br> <br> // Messaggio di conferma (potete cambiarlo)<br> try<br> {<br> await MyCall.AssureMedia()<br> .ContinueWith(_ => MyCall.PlayPrompt(ext.GetPropertyValue("PROMPTSETID"), ["ST_CUSTOM1_SET"], PlayPromptOptions.Blocked))<br> .Unwrap();<br> }<br> catch<br> {<br> // Se non va il messaggio pazienza, l'importante è che funzioni<br> }<br> <br> return true;<br> }<br> catch (Exception ex)<br> {<br> MyCall.Info($"Errore: {ex.Message}");<br> return false;<br> }<br> }<br> <br> return false;<br> }<br> }<br>}<br>
Passo 4: Script per Disattivazione (9998)
Stesso procedimento, ma con codice 9998:using System;<br>using System.IO;<br>using System.Threading.Tasks;<br>using TCX.Configuration;<br>using CallFlow;<br>using System.Linq;<br>using System.Collections.Generic;<br><br>namespace dummy<br>{<br> public class DisattivaRiunioni : ScriptBase<DisattivaRiunioni><br> {<br> public override async Task<bool> StartAsync()<br> {<br> const string FlagPath = "/var/lib/3cxpbx/Instance1/Data/Logs/RIUNIONE_ATTIVA.flag";<br> <br> if (MyCall.Caller.DN is Extension ext && MyCall.ReferredByDN is not DN)<br> {<br> try<br> {<br> if (File.Exists(FlagPath))<br> {<br> File.Delete(FlagPath);<br> MyCall.Info($"Riunioni disattivate da interno {ext.Number}");<br> }<br> <br> try<br> {<br> await MyCall.AssureMedia()<br> .ContinueWith(_ => MyCall.PlayPrompt(ext.GetPropertyValue("PROMPTSETID"), ["ST_CUSTOM2_SET"], PlayPromptOptions.Blocked))<br> .Unwrap();<br> }<br> catch<br> {<br> // Stesso discorso di prima<br> }<br> <br> return true;<br> }<br> catch (Exception ex)<br> {<br> MyCall.Info($"Errore: {ex.Message}");<br> return false;<br> }<br> }<br> <br> return false;<br> }<br> }<br>}<br>
Come Testarlo
- Componete 9999 - dovreste sentire "Profilo personalizzato 1 imposta stato" (o il vostro messaggio custom)
- Fate chiamare qualcuno da fuori - dovrebbe sentire il vostro messaggio riunioni
- Componete 9998 - si disattiva
- Fate richiamare - dovrebbe squillare normalmente
Personalizzazioni Utili
Cambiare i Codici
Facilissimo: quando create gli script, mettete il codice che preferite invece di 9999/9998.Messaggi Audio Personalizzati
Ho trovato tre modi che funzionano:Opzione 1 - Prompt di Sistema (la più affidabile): Andate in Impostazioni → Prompt di sistema → Italian Prompts Set e cercate ST_CUSTOM1_SET e ST_CUSTOM2_SET. Sostituite quei file con i vostri messaggi tipo "Modalità riunioni attivata" e "Modalità riunioni disattivata".
Opzione 2 - File negli Script: Se vedete una sezione "File audio" sotto l'editor di codice degli script, potete caricare lì i vostri file e modificare il codice per usarli:
["nome_vostro_file.wav"] // con .wav<br>
Opzione 3 - Cambiare il messaggio principale: Nell'IVR Riunioni potete sempre cambiare il file audio con quello che preferite.
Specifiche per i File Audio
Dopo vari tentativi, questi formati funzionano sempre:- WAV, 8kHz, Mono, 16-bit
- Durata massima 30 secondi per i prompt, 60 per l'IVR
- Evitate file troppo grossi
Note Finali
Questo sistema l'ho sviluppato perché quello che trovavo in giro era troppo invasivo o non funzionava bene. I vantaggi principali:- Non tocca le configurazioni esistenti
- È completamente reversibile
- Non interferisce con il vero fuori orario
- È semplice da usare