Comment

Comment by benzofuran

Here's a gmail extractor google apps script that does the same thing, but for not $30!

function autoExtractGmailMedia() { const START_TIME = Date.now(); const TIME_LIMIT = 1000 * 60 * 5; // 5 minutes (leaves a 1-minute safety buffer) const BATCH_SIZE = 50; const FOLDER_NAME = "Gmail_Media_Export";

  // 1. Retrieve our current position from the script's property store
  const scriptProperties = PropertiesService.getScriptProperties();
  let startIndex = parseInt(scriptProperties.getProperty('START_INDEX')) || 0;

  // Locate or create the destination folder
  let folder;
  const folders = DriveApp.getFoldersByName(FOLDER_NAME);
  if (folders.hasNext()) {
    folder = folders.next();
  } else {
    folder = DriveApp.createFolder(FOLDER_NAME);
  }

  // 2. Process batches in a loop
  // Updated search query to ignore USPS and non-primary tabs
  const searchQuery = 'has:attachment -from:usps.gov -category:promotions -category:updates -category:social (filename:jpg OR filename:jpeg OR filename:png OR filename:zip)';
  
  while (true) {
    const threads = GmailApp.search(searchQuery, startIndex, BATCH_SIZE);
    
    // If the search returns empty, we've hit the end of the inbox
    if (threads.length === 0) {
      console.log("Extraction completely finished.");
      scriptProperties.deleteProperty('START_INDEX');
      deleteTriggers(); // Clean up so it doesn't keep running
      return;
    }

    for (let i = 0; i < threads.length; i++) {
      const messages = threads[i].getMessages();
      for (let j = 0; j < messages.length; j++) {
        const attachments = messages[j].getAttachments();
        for (let k = 0; k < attachments.length; k++) {
          const attachment = attachments[k];
          const name = attachment.getName().toLowerCase();
          const type = attachment.getContentType();
          const size = attachment.getSize();
          
          // Apply size threshold (102,400 bytes = 100KB) and file type filter
          if (size > 102400) {
            if (type.includes('image/') || name.endsWith('.zip')) {
              folder.createFile(attachment);
            }
          }
        }
      }
    }

    // Increment our starting index for the next batch
    startIndex += BATCH_SIZE;

    // 3. The Stopwatch Check
    if (Date.now() - START_TIME > TIME_LIMIT) {
      console.log(`Approaching timeout at index ${startIndex}. Saving state and passing the baton...`);
      scriptProperties.setProperty('START_INDEX', startIndex.toString());
      scheduleNextRun();
      return; // Exit to avoid the hard timeout exception
    }
  }
}

function scheduleNextRun() { deleteTriggers(); // Prevent duplicate overlapping triggers ScriptApp.newTrigger("autoExtractGmailMedia") .timeBased() .after(60 * 1000) // Fire a new execution 1 minute from now .create(); }

function deleteTriggers() { const triggers = ScriptApp.getProjectTriggers(); for (let i = 0; i < triggers.length; i++) { if (triggers[i].getHandlerFunction() === "autoExtractGmailMedia") { ScriptApp.deleteTrigger(triggers[i]); } } }

parent benzofuran 4 months ago

Discussion

Live from the official Hacker News API

A modern reader for Hacker News · SvelteKit + Skeleton