Javascript, HTML, CSS e... !
10 commenti

Una panoramica ai metodi principali di Gmail Service su un caso pratico d'uso

Recuperare dati da Gmail con Google Apps Script

L'interfaccia browser di Gmail è ricca di componenti aggiuntivi che consentono di gestire la propria casella di posta, ad esempio per, cercare, ordinare, archiviare ed eliminare tutto ciò che si desidera in un paio di clic così come organizzare la propria posta creando filtri, etichette e molto altro.

Se ci fermiamo a pensare a cosa potremo realizzare di nuovo con Google Apps Script per poterlo implementare in questa moltitudine di funzionalità, sono convinto che le idee, anche per te, come per me, iniziano a scarseggiare. Tuttavia, con Google Apps Script, possiamo sicuramente coordinare ed automatizzare il controllo delle funzionalità già esistenti!

Come premessa per un approccio funzionale all'utilizzo di Google Apps Script per la gestione dei dati di Gmail partirei da una condizione nota in cui tutti noi, chi più chi meno, ci ritroviamo inevitabilmente ad avere. Gli account di posta Gmail (così come quelli di qualsiasi altro provider) se utilizzati senza un criterio organizzativo sono soggetti mese dopo mese a diventare un grande calderone di messaggi nel quale diventa difficile districarsi ed andare in un secondo momento a ritrovare comunicazioni ed allegati importanti.

Facciamo quindi un semplice esempio sul quale baseremo il seguente tutorial.
Poniamo di voler tenere sotto controllo i messaggi provenienti da un determinato indirizzo di posta, ad esempio quello di un tuo collaboratore, e considerando che questa persona ti invia materiale ed altra documentazione in modo discontinuo suddividendola in diverse mail nel corso del tempo, data l'importanza di non perdere alcuna di queste informazioni al fine di portare avanti i progetti comuni per un ipotetico cliente, quello di cui avresti bisogno è raggruppare i suoi messaggi per concentrare in un unica mail tutte le informazioni e tutti gli allegati presenti che ha sparso nei messaggi in un determinato periodo.
Per avere maggiore evidenza di questi messaggi riepilogativi può essere utile, inoltre, impostare un filtro nella propria casella di posta Gmail che contrassegni automaticamente con un'etichetta personalizzata ognuno di questi messaggi di riepilogo.

Una panoramica ai metodi principali di Gmail Service atti allo scopo.
Uno dei metodi senz'altro utili quando si lavora con Gmail da Apps Script è il metodo getInboxThreads() che, come è possibile approfondire dalla documentazione, può essere utilizzato così com'è oppure passandogli due parametri. Nel primo caso vengono recuperati tutti i thread (ovvero gli argomenti principali che possono a sua volta essere costituiti da uno o più messaggi) nel secondo invece è possibile indicare l'indice del thread da cui partire (lo 0 indica il thread più recente) ed il numero massimo di argomenti da recuperare. Questo permette di eseguire le operazioni su porzioni ridotte dei messaggi considerando che, in caso di molti messaggi presenti nella casella di posta, il recupero dei dati da Gmail può impiegare diverso tempo, rischiando il timeout dello script o comunque di eccedere i limiti di tempo di esecuzione di Apps Script (Exceeded execution time limit).
Come anticipato, un thread è composto da uno o più messaggi, per ottenere l'oggetto 'messaggio' utilizzeremo i metodi della classe GmailMessage in modo da recuperarne il suo contentuto testuale, il mittente, gli allegati, ecc...

Proviamo a mettere in pratica l'uso di questi metodi per realizzare uno script che soddisfi la situazione del caso di esempio precedentemente proposto, in particolare per andare a leggere gli ultimi 30 thread all'interno della propria casella di posta elettronica, recuperare il contenuto dei relativi messaggi qualora il mittente fosse quello desiderato ed inviare a noi stessi una mail con all'interno allegato un file in formato PDF contenente il testo dei messaggi del mittente in oggetto (con data e nome di riferimento agli eventuali allegati), tutti gli allegati dei vari messaggi nel suo formato originale ed assegnando un'etichetta identificativa creata dinamicamente.

Il codice completo che svolge tutte queste operazioni è il seguente:

function getAttachAndBody(){
  
  var mail_to_check = 'michele.pis...'; // inserire l'indirizzo di posta completo del mittente (da notare che non necessariamente è la sola mail ma può essere il nome completo visualizzato nel mittente)
  var mail_used = Session.getEffectiveUser().getEmail();
  
  var firstPartOfThread = GmailApp.getInboxThreads(0,30);
  var firstPartOfThread_length = firstPartOfThread.length;
  var message, sender_topic, sender_msg, attach, attach_length, message_position, message_length, blnAttach;
  var body = "";
  var attachments = [];
  var attachments_string = "";
  var attach_name_string, attach_name_string_separator;
  var bg_color = "#EEEEEE";
  var bln_msg = false;
  for (var i=0; i<firstPartOfThread_length; i++) {
    message_length = firstPartOfThread[i].getMessages().length;
    for (var j=0; j<message_length; j++) {
      blnAttach = false;
      sender_topic = firstPartOfThread[i].getMessages()[0].getFrom();
      if (sender_topic == mail_to_check) {
        message = firstPartOfThread[i].getMessages()[j];
        sender_msg = firstPartOfThread[i].getMessages()[j].getFrom();
        if (sender_msg.indexOf(mail_used) == -1) {
          bln_msg = true;
          attach = message.getAttachments();
          attach_length = attach.length;
          attach_name_string = "";
          if (attach_length > 0) {
            attach_name_string_separator = "";
            for (y=0; y<attach_length; y++) {
              attachments.push(attach[y]);
              attach_name_string = attach_name_string + attach_name_string_separator + attach[y].getName();
              attach_name_string_separator = ", ";
            }
          }
          body = body + '<tr style="font-size:10px;">';
          body = body + '<td style="border: solid 1px #000; padding: 10px; background-color: ' + bg_color + ';-webkit-print-color-adjust: exact;">' + i + '</td>';
          body = body + '<td style="border: solid 1px #000; padding: 10px; background-color: ' + bg_color + ';-webkit-print-color-adjust: exact;">' + message.getDate() + '</td>';
          body = body + '<td style="border: solid 1px #000; padding: 10px; background-color: ' + bg_color + ';-webkit-print-color-adjust: exact;">' + sender_topic + '</td>';
          body = body + '<td style="border: solid 1px #000; padding: 10px; background-color: ' + bg_color + ';-webkit-print-color-adjust: exact;">' + message.getBody() + '</td>';
          body = body + '<td style="border: solid 1px #000; padding: 10px; background-color: ' + bg_color + ';-webkit-print-color-adjust: exact;">' + attach_name_string + '</td>';
          body = body + '</tr>';
        }
      }
    }
    if (bln_msg) {
      if (bg_color == "#FFFFFF") { bg_color = "#EEEEEE"; } else { bg_color = "#FFFFFF"; }
    }
  }

  body ='<table style="border-collapse: collapse;">' + body + '</table>';
  var bodyDocHtml = DriveApp.createFile('body.html', body, "text/html");
  var bodyId = bodyDocHtml.getId();
  var bodyDocPdf = bodyDocHtml.getAs('application/pdf').getBytes();
  var bodyToSend = {fileName: 'contenuto_delle_mail.pdf', content: bodyDocPdf, mimeType: 'application/pdf'};
  
  attachments.push(bodyToSend);

  if (attachments.length >0) { attachments_string = " insieme ai relativi file allegati"; }
  
  var label_name = 'AppsScript.it';
  createLabel(label_name);
  var label_ref = GmailApp.getUserLabelByName(label_name);
  GmailApp.sendEmail(mail_used, "Ultimi messaggi ricevuti da " + mail_to_check, "Il contenuto dei messaggi si trova allegato in formato pdf" + attachments_string + ".", {attachments: attachments});
  DriveApp.getFileById(bodyId).setTrashed(true);
  var sentThreads = GmailApp.search('from:me to:' + mail_used);
  var mostRecentThread = sentThreads[0];
  label_ref.addToThread(mostRecentThread);
  
}

function createLabel(labelName) {
  if(!GmailApp.getUserLabelByName(labelName)) {
    GmailApp.createLabel(labelName);
  }
}

Come anticipato, con GmailApp.getInboxThreads(0,30), vengono recuperati i 30 thread più recenti sui quali viene effettuato un ciclo al fine di recuperare i messaggi provenienti dal mittente desiderato, nel caso specifico michele.pis (ovvero quello indicato nella variabile 'mail_to_check'), Fig. 1:



messaggi che si intende recuperare tra gli ultimi 30 thread ricevuti

Fig. 1 - Messaggi che si intende recuperare tra gli ultimi 30 thread ricevuti


Il ciclo effettua un doppio controllo in modo da ottenere, per ciascun thread, tutti i vari messaggi ad eccezione delle risposte del destinatario (ovvero quello indicato nella variabile 'mail_used' valorizzata con Session.getEffectiveUser().getEmail() che rappresenta la mail dell'utente che esegue lo script di Apps Script) tramite il metodo getFrom(). Le risposte del destinatario sono comunque visibiliall'interno del messaggio di risposta del mittente.

Per i messaggi che rispettano le condizioni all'interno del ciclo viene effettuato un controllo, con getAttachments(), sulla presenza o meno, di allegati che, in caso positivo, vengono inseriti all'interno di un array (attachments.push(attach[y])) e per ognuno di questi messaggi viene creata una riga di una tabella HTML (con colori alternati per ciascun thread) costituita da 5 colonne: l'id della posizione del thread, la data del messaggio, il mittente, il contenuto testuale del messaggio ed i nomi degli allegati presenti in quel determinato mesaggio.

Con la seguente porzione di codice viene creato un file in formato PDF, il cui contenuto è rappresentato dalla tabella HTML sopra menzionata, che viene inserito all'interno dell'array attachments pronto per essere allegato al nostro messaggio principale insieme agli altri file eventualmente presenti nei messaggi processati:

body ='<table style="border-collapse: collapse;">' + body + '</table>';
var bodyDocHtml = DriveApp.createFile('body.html', body, "text/html");
var bodyId = bodyDocHtml.getId();
var bodyDocPdf = bodyDocHtml.getAs('application/pdf').getBytes();
var bodyToSend = {fileName: 'contenuto_delle_mail.pdf', content: bodyDocPdf, mimeType: 'application/pdf'};

attachments.push(bodyToSend);

Un esempio del file PDF contenente la tabella HTML valorizzata con i campi precedentemente indicati è quello in Fig. 2:



tabella html con i dati dei messaggi processati inserita in un file pdf

Fig. 2 - Tabella HTML con i dati dei messaggi processati inserita in un file PDF da allegare alla mail principale


Successivamente viene creata un'etichetta (qualora non fosse già presente) chiamata 'AppsScript.it' da assegnare al messaggio appena inviato con GmailApp.sendEmail il cui mittende risulta corrispondere a quello del contenuto della variabile 'mail_used'.
Il messaggio inviato, Fig. 3, conterrà pertanto in allegato il file PDF, con le informazioni dei messaggi recuperati, insieme agli altri allegati eventualmente presenti nei vari messaggi il cui riferimento al messaggio corrispondente è dato dal nome dell'allegato originale presente nell'ultima colonna della tabella nel file PDF.



messaggio riepilogativo inviato contenente gli allegati originali dei vari messaggi ed il file pdf con le informazioni di ciascun messaggio processato

Fig. 3 - Messaggio riepilogativo inviato a se stessi contenente gli allegati originali dei vari messaggi ed il file PDF con le informazioni di ciascun messaggio processato


Lo script in questione è puramente illustrativo delle potenzialità che possono essere sfruttate a nostro vantaggio facendo buon uso delle classe e dei metodi di Gmail Service, e pertanto può essere ulteriormente migliorato, ad esempio, inserendo su Google Drive i vari allegati e riportando nel file PDF il relativo url di riferimento (in questo modo si possono evitare potenziali mancati invii delle mail dovuti ad un'eventuale eccessiva dimensione dei file), può inoltre essere inserito un sistema di cache (fare riferimento all'articolo "Memorizzare le risorse nella Cache e condividerle tra un'esecuzione e l'altra") per effettuare più cicli (a slot di 30 thread) e processare così molti più messaggi senza il rischio di eccedere i limiti di esecuzione dello script, ecc...

In definitiva di possibilità ce ne sono molte e grazie alla flessibilità di Google Apps Script diventa semplice districarsi in uno strumento complesso come Gmail rendendolo calzante alle più disparate esigenze. 

Per approfondimenti sulle classi e i metodi di Gmail Service rimando alla documentazione ufficiale.

Tags

Michele Pisani

Michele Pisani

Sviluppatore Javascript ed esperto in Digital Analytics

L'esperienza nel settore Digital Analytics unita ad anni di sviluppo in Javascript ha trovato la massima espressione in Google Apps Script che mi ha permesso, con estrema facilità e poche righe di codice, di realizzare potenti applicazioni interattive e processi automatizzati integrati con i prodotti della G Suite.

Come contattarmi
scrivi un commento

10 Commenti

  1. martedì 6 febbraio 2018 alle ore 17.04 Mauro

    Buongiorno ho trovato uno script che mi scarica in automatico gli allegati su una cartella , e possibile rinominare questi allegati in maniera automatica (nel mio caso mi servirebbe inserire la data odierna davanti a nome dell'allegato) lo script "incriminato" e' questo

    var desc = message.getSubject() + " #"+ message.getId();
    var att = message.getAttachments();
    for (var z=0; z<att.length; z++)
    {
    try {
    file = folder.createFile(att[z]);
    file.setDescription(desc);
    }
    catch (e) { Logger.log(e.toString()); }
    }


    mi potrebbe dare un mano o un suggerimento mi farebbe un grosso favore grazie infinite

    Rispondi a questo commento
    • martedì 6 febbraio 2018 alle ore 21.04 Michele PisaniAutore

      Ciao Mauro,
      puoi provare a creare una funzione che ti restituisce la data del giorno corrente, ad esempio:

      function getTodayDate() {
      var td = Utilities.formatDate(new Date(), "GMT+1", "yyyy-MM-dd");
      return td;
      }


      Dopodiché concatenare la stringa della data al file che crei con una sintassi simile alla seguente, cambiando questa riga di codice:

      file = folder.createFile(att[z]);


      in questa:

      file = folder.createFile(att[z].setName(getTodayDate() + "_" + att[z].getName());


      Se il tuo file originale si chiama ad esempio 'allegato01.zip', dopo questa elaborazione si chiamerà:
      '2018-02-06_allegato01.zip'.

      Spero di aver risposto alla tua domanda.

      Rispondi a questo commento
      • mercoledì 7 febbraio 2018 alle ore 14.57 mauro

        Gentilissimo prorio quello che mi serviva grazie infinite

      • mercoledì 7 febbraio 2018 alle ore 16.20 Michele PisaniAutore

        Di niente, grazie a te per il feedback.
        Un saluto e buon lavoro!

  2. venerdì 21 dicembre 2018 alle ore 13.56 ANTONIO

    Buongiorno Michele
    premetto che da pochissimo mi sto affacciando al mondo di google trovandolo veramente interessante. Sto provando utilizzare lo script da lei indicato ma vedo che purtroppo il pdf che mi viene trasmesso è vuoto. Oltre all'indirizzo mail della persona da cui riceviamo eventuali comunicazioni e che è da inserire, devo anche andare ad effettuare altre modifiche?
    La ringrazio per la disponibilità. Continuerò sicuramente a seguirla.

    Rispondi a questo commento
    • venerdì 21 dicembre 2018 alle ore 23.41 Michele PisaniAutore

      Ciao Antonio,
      a parte l'indirizzo email del mittente desiderato, l'unica altra modifica (eventuale) può essere quella del valore passato come parametro a GmailApp.getInboxThreads(0,30). Nel caso dell'esempio vengono recuperati i 30 thread più recenti sui quali viene effettuato un ciclo al fine di trovare i messaggi provenienti dal mittente in questione.
      Il fatto che tu ottenga un pdf vuoto potrebbe essere dovuto all'assenza di messaggi del mittente desiderato tra gli ultimi 30 che hai ricevuto.
      Per capire se il problema potrebbe essere effettivamente quello puoi fare una prova con un altro mittente che ti ha inviato una email di recente e vedere se il pdf viene popolato.
      Puoi anche aumentare quel valore da 30 ad esempio a 60 per processare un numero maggiore di email facendo attenzione a non aumentarlo troppo per non eccedere i limiti di tempo di esecuzione dello script.

      Rispondi a questo commento
  3. mercoledì 29 maggio 2019 alle ore 17.16 Luigi

    Sembra che alcuni metodi siano deprecati e quindi non funziona pur non dando errore nella creazione della tabella e quindi nell'inserimento dei contenuti.

    Rispondi a questo commento
    • mercoledì 29 maggio 2019 alle ore 20.09 Michele PisaniAutore

      Ciao Luigi,
      ho preso in carico la tua segnalazione ed ho effettuato dei test sullo script.
      Ti assicuro che funziona correttamente e non contiene metodi deprecati (o quantomeno quelli utilizzati funzionano).

      Ho notato però una cosa, che quando ho creato la guida non mi è capitato ma che potrebbe essere il motivo per il quale hai detto che non funziona. Ovvero, nell'articolo ho scritto che il valore della variabile mail_to_check è l'indirizzo di posta completo del mittente, in realtà non è sempre così ed in effetti per alcuni indirizzi sui quali ho effettuato una prova ho ricevuto una mail di riepilogo vuota anziché con il testo dei messaggi. Ho capito però dove sta l'anomalia. In pratica il suo contenuto non deve essere l'indirizzo di posta del mittente, ma il nome completo del mittente stesso sulla base di come questo è salvato.
      Faccio un esempio:

      Se ricevo una mail da Mario Rossi e nel nome del mittente ho il seguente testo:

      Mario Rossi <mario.rossi@test.com>

      Non devo inserire il solo indirizzo mail nella variabile:
      var mail_to_check = "mario.rossi@test.com" ; // <-- non corretto

      Bensì il nome completo:
      var mail_to_check = "Mario Rossi <mario.rossi@test.com>" ;// <-- corretto

      Fammi sapere se così ti funziona, ma direi che il motivo del problema che hai incontrato era proprio questo.

      Rispondi a questo commento
  4. mercoledì 2 ottobre 2019 alle ore 09.43 Ettore Zampetti

    È possibile inserire nella funzione far aggiungere l'evento di una specifica email al calendario?

    Rispondi a questo commento

Scrivi un commento

Il tuo indirizzo email non sarà pubblicato.I campi contrassegnati da un * sono obbligatori
Puoi utilizzare i seguenti tag nei commenti:
[bold]testo[/bold] se vuoi evidenziare un testo con il grassetto[code]function helloworld() { }[/code] se vuoi pubblicare una porzione di codice[url]https://www.appsscript.it[/url] se devi riferirti ad un indirizzo web