Filtri avanzati su gmail

Tags: 

Il sistema di filtri base di Gmail è estremamente limitato: non essendoci i concetti di ordine/priorità tra filtri e di terminazione dell'esecuzione della catena di filtri non è possibile creare dei flussi personalizzati. A questo si aggiunge il sistema di raggruppamento dei messaggi in thread (i filtri si applicano sempre sull'intero thread e non sul singolo messaggio), e la gestione delle espressioni che, per quanto piuttosto flessibile, non sempre riesce a risolvere tutti i problemi.

Se abbiamo tonnellate di mail da smistare, e regole complesse per farlo, i filtri di gmail potrebbero quindi non essere sufficienti.

Ma con un pò di buona volonta possiamo sopperire a questa mancanza attraverso un sistema di filtri "esterno". In particolare, possiamo usare le funzioni di Scripting che ci ci mette a disposizione google con i Google Apps Script.

Questo sistema permette di interagire a basso livello con tantissimi servizi dell'universo google, attraverso degli script javascript. Le possibilità sono enormi, e in questo caso le useremo per smistare la posta come vogliamo noi.

In questo articolo viene proposta una vera e propria libreria di gestione filtri, che chiamo Gmail Advanced Filters.

Installazione

1) Andate nella vostra casella google mail

2) Accedete a Google Drive usando la barra in alto

3) Create un nuovo script premendo su "Crea", "Script", "Crea Script per: Gmail"

4) Cancellate il codice esistente e inserite quello qui sotto:

/**
 * Google Advanced Filters v1.0.20130105
 * by Eric Berdondini
 * http://myhq.it/google-advanced-filters
 */
 
// Label che viene impostata nei nuovi messaggi in ingresso. Es: "Root/Child")
var PROC_LABEL = "_PROC_";
var PROC_DONE_LABEL = false; //"_PROCDONE_";
 
var TEST_MODE = false;
var VERBOSE = true;
 
// Numero massimo di thread elaborati a ogni esecuzione
var MAX_THREADS_PROCESSED = 20;
 
var currentThread = null;
var currentMessage = null;
var currentThreadMessage = null;
var threadLabelsLoaded = {};
var labelsPool = {};
 
var starttime = null;
 
// -------------------------------------------------------------------------------------------
 
function _processThread(thread) {
  // ...
}
 
// -------------------------------------------------------------------------------------------
// FILTRI E AZIONI
 
function fromIs(email) {
  return _emailIs(currentThreadMessage.getFrom(), email);
}
 
function fromMatch(match) {
  return currentThreadMessage.getFrom().match(match);
}
 
function toIs(email) {
  return _emailIs(currentThreadMessage.getTo(), email);
}
 
function toMatch(match) {
  return currentThreadMessage.getTo().match(match);
}
 
function hasLabel(labelname) {
  return _hasGmailLabel(currentThread, labelname);
}
  
function subjectStartsWith(t) {
  return currentThreadMessage.getSubject().indexOf(t) == 0;
}
 
function doArchive() {
  if (!TEST_MODE)
    currentThread.moveToArchive();
  logVerbose("archive");
}
 
function doDelete() {
  if (!TEST_MODE)
    currentThread.moveToTrash();
  logVerbose("delete");
}
 
function doRead() {
  if (!TEST_MODE)
    currentThread.markRead();
  logVerbose("read");
}
 
function doUnimportant() {
  if (currentThread.isImportant()) {
    if (!TEST_MODE)
      currentThread.markUnimportant();
    logVerbose("unimportant");
  }
}
 
function doSetLabel(labelname) {
  _delAllGmailLabel(currentThread);
  _addGmailLabel(currentThread, labelname);
}
 
function doAddLabel(labelname) {
  _addGmailLabel(currentThread, labelname);
}
 
function doRemoveLabel(labelname) {
  _delGmailLabel(currentThread, labelname);
}
 
function doRemoveAllLabels() {
  _delAllGmailLabel(currentThread);
}
 
function doFixLabel(labelname) {
  if (hasLabel(labelname))
    doSetLabel(labelname);
}
 
function _emailIs(messageEmail, match) {
  return messageEmail == match || messageEmail.indexOf("<" + match + ">") >= 0 || messageEmail.indexOf(match) == 0 || messageEmail.indexOf(", " + match) > 0;
}
 
// -------------------------------------------------------------------------------------------
 
/**
 * Funzione da eseguire via trigger
 */
function process() {
  starttime = Date.now();
  var label = _getGmailLabel(PROC_LABEL);
  //var labelDone = PROC_DONE_LABEL ? _getGmailLabel(PROC_DONE_LABEL) : false;
  var threads = label.getThreads(0, MAX_THREADS_PROCESSED);
  Logger.log("THREADS TO BE PROCESSED: " + threads.length);
  //var st = Date.now();
  var messages = GmailApp.getMessagesForThreads(threads);
  //logVerbose("GmailApp.getMessagesForThreads", st);
  
  for (var i = 0; i < threads.length; i++) {
    currentThread = threads<img class="icon" src="/sites/all/libraries/fugue/icons/information-button.png" width="16" height="16" alt="i" />;
    currentThreadMessage = messages<img class="icon" src="/sites/all/libraries/fugue/icons/information-button.png" width="16" height="16" alt="i" />[0];
    currentMessage = messages<img class="icon" src="/sites/all/libraries/fugue/icons/information-button.png" width="16" height="16" alt="i" />[messages[i].length - 1];
    logInfo("OPEN THREAD: " + currentThreadMessage.getId() + " | " + currentThreadMessage.getSubject() + " | " + currentThreadMessage.getTo());
    _processThread(threads<img class="icon" src="/sites/all/libraries/fugue/icons/information-button.png" width="16" height="16" alt="i" />);
    
    _delGmailLabel(currentThread, PROC_LABEL);
    
    if (PROC_DONE_LABEL) {
      _addGmailLabel(currentThread, PROC_DONE_LABEL);
    }
 
    logInfo("CLOSE THREAD: " + currentThread.getFirstMessageSubject());
  }
  _applyGmailLabels();
};
 
// -------------------------------------------------------------------------------------------
 
function _getGmailLabels(thread) {
  var id = thread.getId();
  if (id in threadLabelsLoaded) {
    var labels = {};
    for (var labelname in labelsPool) {
      if (labelsPool[labelname].threads[id] && labelsPool[labelname].threads[id].current)
        labels[labelname] = labelsPool[labelname].object;
    }
    return labels;
  } else {
    var labels = thread.getLabels();
    var result = {};
    for (var i = 0; i < labels.length; i++) {
      if (!labelsPool[labels[i].getName()])
        labelsPool[labels[i].getName()] = {
          object : labels<img class="icon" src="/sites/all/libraries/fugue/icons/information-button.png" width="16" height="16" alt="i" />,
          threads : {}
        };
      labelsPool[labels[i].getName()].threads[id] = {
        previous : true,
        current : true
      };
      result[labels[i].getName()] = labels<img class="icon" src="/sites/all/libraries/fugue/icons/information-button.png" width="16" height="16" alt="i" />;
    }
    threadLabelsLoaded[id] = thread;
    logVerbose("labels loaded");
  }
}
 
function _getGmailLabel(labelname) {
  if (!(labelname in labelsPool)) {
    //var st = Date.now();
    labelsPool[labelname] = {
      object : GmailApp.getUserLabelByName(labelname),
      threads : {}
    }
    //logVerbose("GmailApp.getUserLabelByName", st);
  }
  return labelsPool[labelname].object;
}
 
function _hasGmailLabel(thread, labelname) {
  var id = thread.getId();
  if (!(id in threadLabelsLoaded))
    _getGmailLabels(thread);
  return labelsPool[labelname] && labelsPool[labelname].threads[id] && labelsPool[labelname].threads[id].current;
}
 
function _addGmailLabel(thread, labelname) {
  var id = thread.getId();
  if (!(id in threadLabelsLoaded))
    _getGmailLabels(thread);
  if (!(labelname in labelsPool))
    _getGmailLabel(labelname);
  if (!labelsPool[labelname].threads[id])
    labelsPool[labelname].threads[id] = {
      previous : false,
      current : true
    };
  else
    labelsPool[labelname].threads[id].current = true;
  logVerbose("add label " + labelname + " (cache)");
}
 
function _delGmailLabel(thread, labelname) {
  var id = thread.getId();
  if (!(id in threadLabelsLoaded))
    _getGmailLabels(thread);
  if (labelsPool[labelname].threads[id] && labelsPool[labelname].threads[id].current)
    labelsPool[labelname].threads[id].current = false;
  logVerbose("del label " + labelname + " (cache)");
}
 
function _delAllGmailLabel(thread) {
  var id = thread.getId();
  if (!(id in threadLabelsLoaded))
    _getGmailLabels(thread);
  for (var labelname in labelsPool) {
    if (labelsPool[labelname].threads[id] && labelsPool[labelname].threads[id].current)
      labelsPool[labelname].threads[id].current = false;
  }
  logVerbose("del all labels (cache)");
}
 
function _applyGmailLabels() {
  for (var labelname in labelsPool) {
    if (labelsPool[labelname].object) {
      var threadsAdd = [];
      var threadsAddLog = "";
      var threadsRemove = [];
      var threadsRemoveLog = "";
      for (var tid in labelsPool[labelname].threads) {
        var data = labelsPool[labelname].threads[tid];
        if (data.current && !data.previous) {
          threadsAdd.push(threadLabelsLoaded[tid]);
          threadsAddLog += tid + " ";
        }
        else if (!data.current && data.previous) {
          threadsRemove.push(threadLabelsLoaded[tid]);
          threadsRemoveLog += tid + " ";
        }
      }
      if (!TEST_MODE) {
        if (threadsAdd.length)
          labelsPool[labelname].object.addToThreads(threadsAdd);
        if (threadsRemove.length)
          labelsPool[labelname].object.removeFromThreads(threadsRemove);
      }
      if (threadsAdd.length)
        logVerbose("label " + labelname + " added to " + threadsAddLog);
      if (threadsRemove.length)
        logVerbose("label " + labelname + " removed from " + threadsRemoveLog);
    }
  }
}
 
// -------------------------------------------------------------------------------------------
 
function logInfo(message) {
  Logger.log("i: " + message + " [" + (Date.now() - starttime) + "ms]");
}
 
function logVerbose(message, st) {
  if (VERBOSE)
    Logger.log("d:   " + message + " [" + (Date.now() - starttime) + "ms]" + (st ? " [" + (Date.now() - st) + "ms]" : " -"));
}

5) Impostate un trigger del processo (menu "Risorse", "Trigger del processo"), con i parametri "process", "basato sul tempo", "timer minuti", "ogni minuto"

6) Impostate il nome "Google Advanced Filter" e salvate. (Più tardi ci occuperemo di configurare i filtri)

7) Tornate su Google Mail, andate in "Impostazioni", "Filtri" e aggiungete questo filtro:

Da "*", Applica l'etichetta "_PROC"
(Nota: Dopo aver messo il filtro "Da" vi mostrerà un popup di avvertimento, andate avanti. L'etichetta "_PROC_" dovrà essere creata.)

L'installazione è terminata!

 

  • Cosa abbiamo ottenuto? Ogni nuovo messaggio che arriverà a Gmail avrà l'etichetta "_PROC_", che in pratica è un "flag" che serve a far capire allo script quali sono i messaggi nuovi.
  • Lo script, eseguito una volta al minuto, prende tutti i messaggi nuovi (etichettati con "_PROC_"), li filtra e quindi toglie l'etichetta "_PROC_".
  • Alcuni avvertimenti:
  • Per come funziona il sistema, potrà capitare che quando andate nella caselle vedrete dei messaggi con etichetta "_PROC_" non ancora smistati. Ignorateli (o leggeteli se volete), dopo al massimo 1 minuti verranno smistati.
    Lo script ogni tanto esce con degli errori "innocui" (nel senso che non pregiudicano il funzionamento). Ve ne accorgerete perchè vi arriveranno delle notifiche in mail (di base 1 al giorno di notte, quando capita). Francamente non ho ancora capito perchè (immagino problemi di multi-threading o qualche bug temporaneo di google scripts). Eventualmente potete disabilitare le notifiche dai trigger dello script.

     

    Configurazione

    Editando lo script è possibile configurare i vari filtri.

    Nella prima parte ci sono alcune opzioni che in genere possono essere lasciate così come sono (ma se volete potete giocarci).

    Nella seconda parte, all'interno di "function _processThread(thread) {", potete inserire il codice dei filtri.

    Il codice non è altro che javascript, che ha a disposizione diverse funzioni di filtraggio e di azioni a disposizione. L'elenco di queste funzioni lo potete vedere nella terza parte dello script, subito sotto a _processThread, e i nomi spero siano auto-documentali :)

  • Da notare che non tutte le funzioni sono testatissime, se avete qualche problema fate sapere.
  • Ecco un esempio di filtro, per capire come funziona:

    function _processThread(thread) {
      if (subjectStartsWith("[LIST]") || subjectStartsWith("List Message:")) {
        doSetLabel("Debug/Lists");
        doArchive();
        doRead();
      }
      if (toIs("my@address.com") && (subjectStartsWith("[LIST1]") || subjectStartsWith("[LIST2]") || subjectStartsWith("[LIST3]")))
        doSetLabel("Varie/Lists");
      doFixLabel("Varie/Lists");
      doFixLabel("Varie");
    }

    Mi soffermo sulla sola funzione doFixLabel, che da sola potrebbe risolvere uno dei problemi più importanti della gestione filtri base: l'assenza di priorità.

    La funzione in se è molto semplice: se il thread ha la label specificata, elimina tutte le altre label.

    Può venirci in aiuto se continuiamo a usare i filtri Gmail, ma abbiamo dei messaggi che soddisfano più regole associate a delle label (di solito delle regole gerarchiche, una piu' restrittiva dell'altra), e noi vogliamo che alla fine il messaggio abbia una sola label (quella più restrittiva). In pratica vogliamo smistare i messaggi in un albero di label in maniera esclusiva (se un messaggio fa parte di una label non fa parte di quella sopra).

    In questo caso e' sufficiente impostare tra i filtri avanzati dei "doFixLabel" ordinati dal più restrittivo al meno restrittivo (Nell'esempio prima "Varie/Lists", e poi il più generico "Varie").

    Grazie a questo sistema in uno dei miei account Gmail continuo a usare i filtri base, e tramite dei soli doFixLabel ordinati per bene ottengo il risultato voluto (i messaggi ben smistati in maniera esclusiva tra le varie label).

    Aggiungi un commento