c:\Abhi\Java\New folder\final.js

 avatar
unknown
javascript
5 months ago
7.3 kB
4
Indexable
const RECIPIENT_COL = "Recipient";
const EMAIL_SENT_COL = "Email Sent";
const SCHEDULE_DATE_COL = "Schedule Date";

const BATCH_SIZE = 1000;
const DAILY_LIMIT = 1000; // Adjust based on G Suite sending limit

// Change sender name
const senderName = 'Tube Boost';

// Adds a custom 'Mail Merge' menu to the Google Sheets UI
function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu('Mail Merge')
    .addItem('Schedule Emails', 'scheduleEmails')
    .addToUi();
}

// Schedule emails based on the schedule date column
function scheduleEmails(sheet = SpreadsheetApp.getActiveSheet()) {
  const dataRange = sheet.getDataRange();
  const data = dataRange.getValues();
  const headers = data.shift(); // First row contains headers
  const scheduleDateColIdx = headers.indexOf(SCHEDULE_DATE_COL);
  const emailSentColIdx = headers.indexOf(EMAIL_SENT_COL);

  const triggers = ScriptApp.getProjectTriggers();
  const out = [];

  // Ensure no duplicate triggers
  triggers.forEach(trigger => {
    if (trigger.getHandlerFunction() === 'sendScheduledEmails') {
      ScriptApp.deleteTrigger(trigger);
    }
  });

  let scheduleDate;
  const now = new Date();
  const rowsToUpdate = []; // To keep track of the rows that need to be updated

  // Loop through the rows and find the earliest unsent scheduled email
  for (let i = 0; i < data.length; i++) {
    const row = data[i];
    const emailSent = row[emailSentColIdx];
    scheduleDate = new Date(row[scheduleDateColIdx]);

    if (!emailSent && scheduleDate > now) {
      // Schedule a trigger to send emails at the earliest schedule date
      ScriptApp.newTrigger('sendScheduledEmails')
        .timeBased()
        .at(scheduleDate)
        .create();

      // Prepare the status message for scheduling
      rowsToUpdate.push([`Scheduled for ${scheduleDate.toLocaleString()}`]);
    } else {
      // Keep existing status if already scheduled or sent
      rowsToUpdate.push([emailSent]);
    }
  }

  // Update the email sent column with the new statuses for all rows
  if (emailSentColIdx >= 0) {
    sheet.getRange(2, emailSentColIdx + 1, rowsToUpdate.length).setValues(rowsToUpdate);
  }
}

// Function to send scheduled emails (triggered by the schedule)
function sendScheduledEmails() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const dataRange = sheet.getDataRange();
  const data = dataRange.getValues();
  const headers = data.shift(); // First row contains headers
  const emailSentColIdx = headers.indexOf(EMAIL_SENT_COL);
  const scheduleDateColIdx = headers.indexOf(SCHEDULE_DATE_COL);
  const recipientColIdx = headers.indexOf(RECIPIENT_COL);

  const now = new Date();
  const out = [];
  let sentEmailsCount = 0; // Initialize sent emails count

  // Fetch Gmail draft (only once)
  const draft = getSingleGmailDraft();

  // Loop through data and send emails
  for (let i = 0; i < data.length && sentEmailsCount < BATCH_SIZE; i++) {
    const row = data[i];
    const recipient = row[recipientColIdx];
    const scheduleDate = new Date(row[scheduleDateColIdx]);
    const emailSent = row[emailSentColIdx];

    if (scheduleDate <= now) {
      Logger.log('sent');
      try {
        // Prepare the personalized email subject and body by replacing placeholders with actual values
        const personalizedEmailSubject = replacePlaceholders(draft.subject, headers, row);
        const personalizedEmailBody = replacePlaceholders(draft.html, headers, row);

        // Send email using the Gmail draft
        GmailApp.sendEmail(recipient, personalizedEmailSubject, draft.text, {
          htmlBody: personalizedEmailBody, // Send personalized email
          attachments: draft.attachments,
          inlineImages: draft.inlineImages,
          name: senderName,
        });

        // Mark email as sent with a success message
        out.push(["Sent Successfully"]);
        sentEmailsCount++; // Increment sent emails count
      } catch (e) {
        Logger.log('Error sending email to ' + recipient + ': ' + e.message);
        out.push([emailSent]); // Keep the old status if there's an error
      }
    } else {
      out.push([emailSent]); // Keep old status for emails not yet due to send
    }
  }

  // Update the email sent column with the statuses for all processed rows
  if (emailSentColIdx >= 0) {
    sheet.getRange(2, emailSentColIdx + 1, out.length).setValues(out);
  }

  // Delete trigger if all emails are sent or limit is reached
  if (sentEmailsCount >= BATCH_SIZE || !hasMoreEmailsToSend()) {
    const triggers = ScriptApp.getProjectTriggers();
    triggers.forEach(trigger => {
      if (trigger.getHandlerFunction() === 'sendScheduledEmails') {
        ScriptApp.deleteTrigger(trigger);
      }
    });
  } else {
    // If there are still unsent emails, create a new trigger for the next batch
    scheduleEmails(sheet);
  }
}

// Check if more emails are left to send
function hasMoreEmailsToSend() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const data = sheet.getDataRange().getValues();
  const headers = data.shift();
  const emailSentColIdx = headers.indexOf(EMAIL_SENT_COL);
  const scheduleDateColIdx = headers.indexOf(SCHEDULE_DATE_COL);
  const now = new Date();

  for (let i = 0; i < data.length; i++) {
    const row = data[i];
    const scheduleDate = new Date(row[scheduleDateColIdx]);
    const emailSent = row[emailSentColIdx];

    if (!emailSent && scheduleDate <= now) {
      return true;
    }
  }
  return false;
}

// Function to retrieve a single Gmail draft (for all emails)
function getSingleGmailDraft() {
  const drafts = GmailApp.getDrafts();
  const draft = drafts[0]; // Get the first available draft (ensure only one draft is in Gmail)

  if (!draft) {
    throw new Error('No draft found.');
  }

  const msg = draft.getMessage();
  const allInlineImages = msg.getAttachments({ includeInlineImages: true, includeAttachments: false });
  const attachments = msg.getAttachments({ includeInlineImages: false });
  const htmlBody = msg.getBody();
  const imgObj = allInlineImages.reduce((obj, i) => (obj[i.getName()] = i, obj), {});

  const imgExp = RegExp('<img.*?src="cid:(.*?)".*?alt="(.*?)"[^\>]+>', 'g');
  const matches = [...htmlBody.matchAll(imgExp)];
  const inlineImagesObj = {};

  matches.forEach(match => inlineImagesObj[match[1]] = imgObj[match[2]]);

  return {
    subject: msg.getSubject(),
    text: msg.getPlainBody(),
    html: htmlBody,
    attachments: attachments,
    inlineImages: inlineImagesObj
  };
}

// Function to replace placeholders in the email body or subject with actual data
function replacePlaceholders(template, headers, row) {
  let result = template;

  // Loop through all the headers and replace the placeholders with actual values
  headers.forEach((header, idx) => {
    const placeholder = `{{${header}}}`; // Create placeholder like {{First name}}
    const value = row[idx]; // Get the actual value from the current row

    result = result.replace(new RegExp(placeholder, 'g'), value || ''); // Replace placeholder with value
  });

  return result; // Return the personalized email body or subject
}
Editor is loading...