Monday 27 April 2015

Archive Gmail messages to Google Drive folders - using DriveApp

In the past we have been using a great script from Marcello Scacchetti from this blog.  The script allows you to archive Gmail messages to Google Drive. Please see that blog for full details.

Since DocsList was depreciated this script no longer works.  I have made a few tweaks to the original to make this work with DriveApp instead.   One problem was the DriveApp does not have an identical equivalent to GetFolderbyName so it was necessary to add a quick custom function to replace this (the "getFolders" function at the end of the script)

Here's the new script...

/**
 * Main function run at spreadsheet opening
 */
function onOpen() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var menuEntries = [ 
    {name: "Initialize", functionName: "init"},
    {name: "Archive Gmail Messages", functionName: "ScanGmail"}
  ];
  ss.addMenu("Gmail Archiver", menuEntries);
}
    
/**
 * Initialize the system
 */
function init() {
    // Create the needed Gmail label
    GmailApp.createLabel("Archive to Drive");
    
    // Create Google Drive folder if doesn't exists
    try {
       var folder = DriveApp.getFolders("Email Archive");
    } catch(e) {
       // Folder doesn't exists
       DriveApp.createFolder("Email Archive");
    }
    
    Browser.msgBox("Created Gmail label: Archive to Drive and Google Drive folder: Email Archive");
}

/**
 * Scan Gmail account for message archive requests
 */
function ScanGmail() {
  // Default Drive folder where to archive messages
  var baseFolder = "Email Archive";
  
  // Get the label
  var label = GmailApp.getUserLabelByName("Archive to Drive");
    var threadsArr = getThreadsForLabel(label);
    for(var j=0; j<threadsArr.length; j++) {
      var messagesArr = getMessagesforThread(threadsArr[j]);
      for(var k=0; k<messagesArr.length; k++) {
        var messageId = messagesArr[k].getId();
        var messageDate = Utilities.formatDate(messagesArr[k].getDate(), Session.getTimeZone(), "dd/MM/yyyy - HH:mm:ss");
        var messageFrom = messagesArr[k].getFrom();
        var messageSubject = messagesArr[k].getSubject();
        var messageBody = messagesArr[k].getBody();
        var messageAttachments = messagesArr[k].getAttachments();
        
        // Create the new folder to contain the message
        var newFolderName = messageDate + " - " + messageFrom + " - " + messageSubject + " - " + messageId;
        var newFolder = createDriveFolder(baseFolder, newFolderName);
        
        // Create the message PDF inside the new folder
        var htmlBodyFile = newFolder.createFile('body.html', messageBody, "text/html");
        var pdfBlob = htmlBodyFile.getAs('application/pdf');
        pdfBlob.setName(newFolderName + ".pdf");
        newFolder.createFile(pdfBlob);
        htmlBodyFile.setTrashed(true);
        

        // Save attachments
        for(var i = 0; i < messageAttachments.length; i++) {
            var attachmentName = messageAttachments[i].getName();
            var attachmentContentType = messageAttachments[i].getContentType();
            var attachmentBlob = messageAttachments[i].copyBlob();
            newFolder.createFile(attachmentBlob);
        }

      }
      // Remove Gmail label from archived thread
      label.removeFromThread(threadsArr[j]);
    }
    Browser.msgBox("Gmail messages successfully archived to Google Drive");
}


/**
 * Find all user's Gmail labels that represent mail message
 * movement requests es: moveto->xx@yyyy.com
 *
 * @return {GmailLabel[]} Array of GmailLabel objects
 */
function scanLabels() {
  // logs all of the names of your labels
  var labels = GmailApp.getUserLabels();
  var results = new Array();
  for (var i = 0; i < labels.length; i++) {
    if(labels[i].getName() == "Archive to Drive") {
      results.push(labels[i]);
    }
  }
  return results;
}

/**
 * Get all Gmail threads for the specified label
 *
 * @param {GmailLabel} label GmailLabel object to get threads for
 * @return {GmailThread[]} an array of threads marked with this label
 */
function getThreadsForLabel(label) {
  var threads = label.getThreads();
  return threads;
}

/**
 * Get all Gmail messages for the specified Gmail thread
 *
 * @param {GmailThread} thread object to get messages for
 * @return {GmailMessage[]} an array of messages contained in the specified thread
 */
function getMessagesforThread(thread) {
  var messages = thread.getMessages();
  return messages;
}


/**
 * Get methods of an object
 * @param {Object} object to scan
 * @return {Array} object's methods
 */
function getMethods(obj) {
  var result = [];
  for (var id in obj) {
    try {
      if (typeof(obj[id]) == "function") {
        result.push(id + ": " + obj[id].toString());
      }
    } catch (err) {
      result.push(id + ": inaccessible");
    }
  }
  return result;
}

/**
 * Create a Google Drive Folder
 *
 * @param {String} baseFolder name of the base folder
 * @param {String} folderName name of the folder
 * @return {Folder} the folder object created representing the new folder 
 */
function createDriveFolder(baseFolder, folderName) {
  var baseFolderObject = getFolders(baseFolder);
  return baseFolderObject.createFolder(folderName);
}

// new function to simulate GetFolderbyName

function getFolders(folderName)
{      
  var folders = DriveApp.getFolders();     
 while (folders.hasNext()) {
   var folder = folders.next();
   if(folderName == folder.getName()) {         
     return folder;
   }
 }
  return null;
}


10 comments:

  1. Since DocsList was depreciated my Script merge Google document from google spreadsheet value dosent fire anymore so if you can help me to solve it i can shaer with you the full script

    ReplyDelete
  2. Hi Chadi - sure will be glad to have a look. Ill send you a message on Google+

    ReplyDelete
  3. Where should i save and run this ?

    ReplyDelete
  4. Hi Kel - instructions for how to deploy this can be found on the original blog.
    http://www.jellybend.com/2012/12/20/archive-gmail-messages-to-google-drive-folders/

    ReplyDelete
  5. I initialzed the script and it added the label to gmail and the folder to Drive but when I click the "Archive gmail messages" I get this error:

    There was a problem
    Script function ScanGmail could not be found

    Do you think this has to do with my company not allowing add-ons or is it something to with my saving, running, publishing of the script?

    ReplyDelete
  6. Almost a year later, I'd love to thank you for your work! Good job on updating a smart script.
    Maybe for some newbies you might add a quick link to some instructions on how to manually change the code.gs in the background.

    For anyone reading and wondering, open Tools->Script Editor and replace the code in the editor with James' code. All of it. Save and return to your copy of Marcello Scacchetti's spreadsheet. Now initialize and follow the instructions given in the original post.

    ReplyDelete
  7. Hmm, the link given:

    http://www.jellybend.com/2012/12/20/archive-gmail-messages-to-google-drive-folders/

    Can not be reached is there a new link?

    ReplyDelete
    Replies
    1. hi Adam, the post is preserved in the internet archive here: https://web.archive.org/web/20160217035624/http://www.jellybend.com/2012/12/20/archive-gmail-messages-to-google-drive-folders/

      Delete
  8. Thank you so much for updating this script Mr. Pearson!

    ReplyDelete