Javascript, HTML, CSS e... !
4 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
  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

4 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!

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]http://www.appsscript.it[/url] se devi riferirti ad un indirizzo web