1

i want 2 mint delay after one alteration of the loop. Means I want to add some delay in sending emails. the complete code link is(https://github.com/googleworkspace/solutions/blob/master/mail-merge/src/Code.js)

   obj.forEach(function(row, rowIdx){
   sleep(1200000);
    // only send emails is email_sent cell is blank and not hidden by filter
    if (row[EMAIL_SENT_COL] == ''){
      try {
        const msgObj = fillInTemplateFromObject_(emailTemplate.message, row);

    // @see https://developers.google.com/apps-script/reference/gmail/gmail-app#sendEmail(String,String,String,Object)
    // if you need to send emails with unicode/emoji characters change GmailApp for MailApp
    // Uncomment advanced parameters as needed (see docs for limitations)
    GmailApp.sendEmail(row[RECIPIENT_COL], msgObj.subject, msgObj.text, {
      htmlBody: msgObj.html,
      // bcc: 'a.bbc@email.com',
      // cc: 'a.cc@email.com',
      // from: 'an.alias@email.com',
      // name: 'name of the sender',
      // replyTo: 'a.reply@email.com',
      // noReply: true, // if the email should be sent from a generic no-reply email address (not available to gmail.com users)
      attachments: emailTemplate.attachments,
      inlineImages: emailTemplate.inlineImages
    });
    // modify cell to record email sent date
    out.push([new Date()]);
  } catch(e) {
    // modify cell to record error
    out.push([e.message]);
  }
} else {
  out.push([row[EMAIL_SENT_COL]]);
}
});
  • I don't see where you are using setTimeout or sleep. But I hope you realize that setTimeout is a client side not a server side function. – Cooper Mar 29 '21 at 18:47

1 Answers1

0

Solution:

You can use an Installable Trigger to run sendEmails() every one minute. This can be created when the menu item is selected:

EDIT: Since everyMinute(2) is not allowed, one workaround is to have the function execute every minute. And since there's a column that gets updated once the email is sent, on first execution it will mark the column as "To Send", and on the 2nd execution it will send the email.


function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu('Mail Merge')
      .addItem('Send Emails', 'createTrigger')
      .addToUi();
}

function createTrigger() {
  ScriptApp.newTrigger("sendEmails")
    .timeBased()
    .everyMinutes(1)
    .create();
}

Then replace the forEach() with a simple for loop so it can break out of it once the first email is marked for sending or sent.

  // loop through the rows of data and break once one email is sent
  for (i = 0; i < obj.length; i++) {
    var row = obj[i];
    // Mark emails with "To Send" if email_sent cell is blank. Only send emails if email_sent cell is "To Send" and not hidden by filter
    if (row[EMAIL_SENT_COL] == ''){
      out.push(['To Send']);
      break;
    } else if (row[EMAIL_SENT_COL] == 'To Send'){
      try {
        const msgObj = fillInTemplateFromObject_(emailTemplate.message, row);

        // @see https://developers.google.com/apps-script/reference/gmail/gmail-app#sendEmail(String,String,String,Object)
        // if you need to send emails with unicode/emoji characters change GmailApp for MailApp
        // Uncomment advanced parameters as needed (see docs for limitations)
        GmailApp.sendEmail(row[RECIPIENT_COL], msgObj.subject, msgObj.text, {
          htmlBody: msgObj.html,
          // bcc: 'a.bbc@email.com',
          // cc: 'a.cc@email.com',
          // from: 'an.alias@email.com',
          // name: 'name of the sender',
          // replyTo: 'a.reply@email.com',
          // noReply: true, // if the email should be sent from a generic no-reply email address (not available to gmail.com users)
          attachments: emailTemplate.attachments,
          inlineImages: emailTemplate.inlineImages
        });
        // modify cell to record email sent date
        out.push([new Date()]);
      } catch(e) {
        // modify cell to record error
        out.push([e.message]);
      }
      break;
    } else {
      out.push([row[EMAIL_SENT_COL]]);
    }
  }

And once all the rows are processed, delete the trigger:

  // updating the sheet with new data
  sheet.getRange(2, emailSentColIdx+1, out.length).setValues(out);

  if (out.length == obj.length) {
    var triggers = ScriptApp.getProjectTriggers();
    for (var j = 0; j < triggers.length; j++) {
    ScriptApp.deleteTrigger(triggers[j]);
    }
  }
CMB
  • 4,950
  • 1
  • 4
  • 16
  • Thank you soo much for your answer but if I remove the forEach loop then how it can get new values from spread sheet and how can we stop it when all (10) emails are sent after 20 mints? you can visit following link for more information about this code (https://developers.google.com/workspace/solutions/mail-merge?authuser=2) – Muhammad Irfan Mar 29 '21 at 18:26
  • I edited my answer. Replaced the forEach with a for loop so execution can be broken with one email sent. Also, delete the trigger once there are no more emails sent. – CMB Mar 29 '21 at 18:59
  • A ton of thanks for you help code is almost working fine with some minor changes but we can't set everyMinutes(2) it showing following Exception. Exception: The value you passed to everyMinutes was invalid. It must be one of 1, 5, 10, 15 or 30. screenshot (https://www.screencast.com/t/qcvpGL6Kzj) if we set it as "everyMinutes(1)" its working fine but if we want to set it 2 mint or 3 mints delay then what can we do. I appreciate your help :) – Muhammad Irfan Mar 30 '21 at 05:59
  • I have edited my answer to include a workaround. Basically the email is marked first as "To Send" on the first minute, then on the second it gets sent and the column is updated with the date. – CMB Mar 30 '21 at 08:11
  • Wow You are a genius bro Thanks a lot. One last thing Please update the code that is for deleteTriggers into the triggers[j] instead of triggers[i]. ScriptApp.deleteTrigger(triggers[i]); Once again A ton of thanks Bro :) – Muhammad Irfan Mar 30 '21 at 10:19
  • Much appreciated! Fixed the answer as well. – CMB Mar 30 '21 at 14:29