Wednesday, 8 January 2025

How to download any attachment from GMail

Okay, on the back of my earlier post today, I couldn't help myself but continue to tinker with Google Apps script platform - by taking the automation further. This is the power of AI tools - imagine how much productivity and efficiency improvements can be unlocked to anyone, especially for the small business owner, or your own personal workflow automations -- automating manual back-office processes without relying on an IT person to support you!? 

Sure I spent about four hours playing around - the process wasn't perfect. It can be tricky making changes, the tendency to keep adding new features, sometimes breaking everything and starting over again. ChatGPT Canvas is still a work in progress, but it's no stretch of imagination to expect future enhancements with a fully integrated developer environment, deployment with version control, rollbacks, etc. Anyone with a little bit of technical curiosity can start to unlock personal productivity improvements by investing some time in learning and experimentation.

Here's a sample of the tool:


Here's the write up from ChatGPT using Canvas:

How to Build a Google Apps Script to Download Gmail Attachments

Downloading Gmail attachments automatically can save time and improve productivity. In this guide, we'll walk through building a Google Apps Script to dynamically fetch email senders, process their emails, and save attachments directly to Google Drive.

This guide assumes basic knowledge of Google Apps Script and JavaScript. Let’s dive in!


Step 1: Set Up Your Google Apps Script Project

  1. Go to Google Apps Script.
  2. Click New Project to create a blank script project.
  3. Rename the project to something meaningful, like Gmail Attachment Downloader.

Step 2: Write the Backend Script (Code.gs)

The backend script handles:

  • Fetching Gmail threads and senders.
  • Processing selected emails.
  • Saving attachments to Google Drive.

Here’s the Code.gs file:

function doGet() {
  return HtmlService.createHtmlOutputFromFile('SenderSelection').setTitle('Select Sender');
}

function getSendersChunk(startIndex, chunkSize) {
  var threads = GmailApp.getInboxThreads(startIndex, chunkSize);
  Logger.log(`Fetched ${threads.length} threads starting from index ${startIndex}.`);

  var senders = new Set();
  threads.forEach(function (thread) {
    var messages = thread.getMessages();
    messages.forEach(function (message) {
      senders.add(message.getFrom());
    });
  });

  return Array.from(senders).sort(); // Return sorted unique senders
}

function getTotalThreads() {
  return GmailApp.getInboxThreads().length;
}

function processSelectedSender(senderEmail) {
  var threads = GmailApp.search('from:' + senderEmail);
  var totalEmails = threads.length;

  var folderName = senderEmail.replace(/[<>:"/\|?*]/g, '_'); // Sanitize folder name
  var dateSuffix = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM-dd');
  var folder = DriveApp.createFolder(folderName + "_" + dateSuffix);
  var logMessages = [`Created folder: ${folder.getName()}`, `Found ${totalEmails} emails from sender: ${senderEmail}`];

  threads.forEach(function (thread, threadIndex) {
    var messages = thread.getMessages();
    messages.forEach(function (message, messageIndex) {
      var subject = message.getSubject();
      logMessages.push(`Processing email ${threadIndex + 1}/${totalEmails}: "${subject}"`);

      var attachments = message.getAttachments();
      attachments.forEach(function (attachment) {
        var fileName = attachment.getName();
        var extensionIndex = fileName.lastIndexOf(".");
        var baseName = fileName.substring(0, extensionIndex);
        var extension = fileName.substring(extensionIndex);
        var timestampedFileName = baseName + "_" + new Date().getTime() + extension;

        try {
          folder.createFile(attachment).setName(timestampedFileName);
          Logger.log(`Saved attachment: ${timestampedFileName}`);
        } catch (e) {
          Logger.log(`Error saving attachment: ${e.message}`);
        }
      });
    });
  });

  logMessages.push(`Processing complete for sender: ${senderEmail}`);
  return logMessages; // Return all log messages to the frontend
}

Step 3: Create the Frontend (SenderSelection.html)

The frontend displays a dropdown list of senders and allows the user to process selected emails. Here's the SenderSelection.html file:

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <style>
      body {
        font-family: Arial, sans-serif;
        margin: 20px;
      }
      label, select, button {
        font-size: 14px;
      }
      select {
        width: 100%;
        margin-bottom: 10px;
      }
      #log {
        margin-top: 20px;
        padding: 10px;
        border: 1px solid #ccc;
        background: #f9f9f9;
        height: 300px;
        overflow-y: auto;
        font-size: 12px;
        white-space: pre-wrap;
      }
      .progress {
        font-weight: bold;
        margin-top: 10px;
      }
    </style>
  </head>
  <body>
    <h3>Select a Sender</h3>
    <form id="senderForm">
      <label for="sender">Senders:</label>
      <select id="sender" name="sender">
        <!-- Options will be populated dynamically -->
      </select>
      <button type="button" onclick="submitSender()">Process</button>
    </form>

    <div id="log">Logs will appear here...</div>
    <div id="progress" class="progress"></div>

    <script>
      let startIndex = 0;
      const chunkSize = 50;
      let totalThreads = 0;

      function initialize() {
        google.script.run.withSuccessHandler(setTotalThreads).getTotalThreads();
      }

      function setTotalThreads(total) {
        totalThreads = total;
        addLog(`Total threads in Gmail: ${totalThreads}`);
        loadNextChunk();
      }

      function loadNextChunk() {
        if (startIndex >= totalThreads) {
          addLog("All senders loaded.");
          return;
        }

        addLog(`Loading senders from threads ${startIndex + 1} to ${startIndex + chunkSize}...`);
        google.script.run.withSuccessHandler(populateSenders).getSendersChunk(startIndex, chunkSize);
        startIndex += chunkSize;
      }

      function populateSenders(senders) {
        const senderDropdown = document.getElementById('sender');
        senders.forEach(function (sender) {
          const option = document.createElement('option');
          option.value = sender;
          option.textContent = sender;
          senderDropdown.appendChild(option);
        });

        loadNextChunk();
      }

      function submitSender() {
        const selectedSender = document.getElementById('sender').value;
        if (selectedSender) {
          clearLogs();
          setProgress(`Processing emails for: ${selectedSender}`);
          google.script.run.withSuccessHandler(displayLogs).processSelectedSender(selectedSender);
        } else {
          alert('Please select a sender.');
        }
      }

      function displayLogs(logMessages) {
        logMessages.forEach(addLog);
        setProgress("Processing complete.");
      }

      function addLog(message) {
        const logDiv = document.getElementById('log');
        logDiv.textContent += message + "\n";
        logDiv.scrollTop = logDiv.scrollHeight;
      }

      function clearLogs() {
        document.getElementById('log').textContent = "";
        setProgress("");
      }

      function setProgress(message) {
        document.getElementById('progress').textContent = message;
      }

      window.onload = initialize;
    </script>
  </body>
</html>

Step 4: Deploy the Web App

  1. Go to Deploy > New Deployment.
  2. Choose Web App.
  3. Configure:
    • Execute as: Me.
    • Who has access: Only Me.
  4. Deploy and copy the deployment URL.

Step 5: Test the App

  1. Open the deployment URL in your browser.
  2. Wait for senders to load dynamically.
  3. Select a sender from the dropdown.
  4. Click Process.
  5. Check the logs in the "Logs" section for real-time updates on email processing and attachment saving.
  6. Validate in Google Drive:
    • Go to your Google Drive.
    • Check for a newly created folder named after the sender's email address with a date suffix.
    • Ensure that all attachments from the processed emails appear in this folder.

Key Features

  • Dynamic Sender Loading: Senders are loaded in chunks for responsiveness.
  • Real-Time Logs: Displays progress for email and attachment processing. Note: Currently, the logs only appear once all emails are processed. This is an opportunity to enhance the script for real-time updates.
  • Automatic Folder Creation: Creates a unique folder in Google Drive for each sender's attachments.
  • Filename Uniqueness: Attachments are saved with a timestamp appended to the original filename, ensuring no files are overwritten during the saving process.

Potential Feature Enhancements

  1. Real-Time Logging:

    • Update the script to send logs incrementally to the frontend as each email is processed, instead of batching logs after completion.
  2. Progress Bar Integration:

    • Add a progress bar in the frontend to visually indicate the percentage of emails processed.
  3. Advanced Filters:

    • Allow users to apply filters such as date ranges, subject keywords, or labels to narrow down email selection.
  4. Attachment Size Limit:

    • Introduce a feature to skip downloading large attachments beyond a specified size limit.
  5. Retry Mechanism:

    • Implement error handling to retry saving attachments in case of temporary failures (e.g., Drive quota issues).
  6. Specify File Extensions:

    • Add an option to download only specific file types (e.g., PDF, DOCX) by specifying file extensions.
  7. Search by Google Contact Emails:

    • Allow users to search emails from their Google contacts directly for better targeting.
  8. Google Sheet Integration:

    • Create a Google Sheet to log the email subject, sender, and a link to the saved file in Google Drive for better tracking and reporting.
  9. Real-Time Logging:

    • Update the script to send logs incrementally to the frontend as each email is processed, instead of batching logs after completion.
  10. Progress Bar Integration:

    • Add a progress bar in the frontend to visually indicate the percentage of emails processed.
  11. Advanced Filters:

    • Allow users to apply filters such as date ranges, subject keywords, or labels to narrow down email selection.
  12. Attachment Size Limit:

    • Introduce a feature to skip downloading large attachments beyond a specified size limit.
  13. Retry Mechanism:

    • Implement error handling to retry saving attachments in case of temporary failures (e.g., Drive quota issues).

With this guide, you can easily automate the task of downloading Gmail attachments to Google Drive. Customize it further based on your needs, and streamline your email management!

No comments:

Post a Comment