If you are a regular here, you may have noticed that our forum  has moved from a google groups forum to a Google+ community.

I had never noticed before, but there is a groups  service in Apps Script that allows you to access things about that group, including the members. Using this allowed me to send a note to all forum members about this change. Here’s how

Using the groups service

This is real straightforward. Assuming you are an administrator of the group, all you need is the email address of the group, and this function will give you an array of email addresses of everyone in the group. Note also that i’m only taking a chunk of emails at a time. This is because the MailApp service has a quota of people to whom it can email each day, so I’m running this over a period of a few days.
function getEmails(groupEmail) {
  var skip =97, limit = 80;
  return GroupsApp.getGroupByEmail(groupEmail).getUsers().map(function(d) {
    return d.getEmail();
  })
  .filter ( function (d,i) {
    return i >= skip && i < skip+limit;
  });
  
}

Using the MailApp service

I wanted to send a customized email to each one, so once the message has been formatted, sending them is just this
function sendEmails (mailOb) {
  
  mailOb.forEach (function(d) { 
    cUseful.rateLimitExpBackoff(function() {
      MailApp.sendEmail(d);
    });
  });

  return mailOb;
}

The email template with html service

I can use Html Service to render a formatted email from a template. I didn’t bother with formal templating for this trivial task – I’m simply going to push each email address into a DOM element to personalize.
Here’s my template index.html
<div style="color:red;" id="results"></div>

<div id="content" style="font-family:sans-serif;">

 <h3>Dear <span id="email"></span></h3>
 <p>
  Since you are a member of the excel liberation forum, I thought I would let you know that this has now migrated to a Google Plus community. 
  You can find it in Google Plus under the community name <a href="https://plus.google.com/communities/103319333735778985706">Desktop Liberation</a>
 </p>

 <p>
  I hope you will join us over there. 
 </p>
 <div>
  <a href="https://plus.google.com/+brucemcpherson">Bruce</a>
 </div>

</div>

<?!= include('email.js') ?>

Running the GAS functions from the html service

This little JavaScript snippet – emails.js executes each of .GS functions (get and send emails).
<script>
window.onload = function() {
  var resultDiv = document.getElementById ("results");
  var contentDiv = document.getElementById ("content");
  var emailDiv = document.getElementById ("email");

   function sendMails (result) {
   
     var messages = result.map (function(d) {
         emailDiv.innerHTML = d;

           return {
             to:d,
             subject:"The Excel Liberation forum has moved to a Google+ community",
             htmlBody:contentDiv.innerHTML,
             body:contentDiv.innerText
           } ; 

     });

     runGooglyThingy ( 'sendEmails' , messages);
   }
   
   runGooglyThingy ( 'getEmails' , 'excel-ramblings@googlegroups.com',sendMails);
   
   function runGooglyThingy ( func, arg ,then) {
     try {
     
       google.script.run.withFailureHandler(function(error) {
         // report error
         resultDiv.innerHTML += ('<br>' + error);
       })
       .withSuccessHandler(function(result) {
        // all was good
        resultDiv.innerHTML = result.length + ' results';
        if (then) {
            then (result);
        }
       })
       [func] (arg);
     }
     catch (error) {
        resultDiv.innerHTML += ('<br>completely failed' + error);
     }
    }
    
};

</script>
It’s worth a few words on google.script.run(). I need to orchestrate 2 of these, and they run asynchronously. In a normal app I would have used ES6 Promises along with a shim for older browsers, but caja doesn’t like them, and I didn’t want to bring in jQuery just for that, so you’ll see that runGooglyThingy() takes an optional additional argument of what to run next. This is pretty simple way of orchestrating asynchronous tasks if you don’t have Promises available.
That’s all. If you got one of these emails, that’s how you got it. If you didn’t why not head over to the desktop liberation google plus community and join up.

Here’s the completed code.js

function doGet() {
  return HtmlService.createTemplateFromFile('index').evaluate();
}
                       
function sendEmails (mailOb) {
  
  mailOb.forEach (function(d) { 
    cUseful.rateLimitExpBackoff(function() {
      MailApp.sendEmail(d);
    });
  });

  return mailOb;
}

function getEmails(groupEmail) {
  var skip =97, limit = 80;
  return GroupsApp.getGroupByEmail(groupEmail).getUsers().map(function(d) {
    return d.getEmail();
  })
  .filter ( function (d,i) {
    return i >= skip && i < skip+limit;
  });
  
}
/**
 * Returns the contents of an HTML file.
 * @param {string} file The name of the file to retrieve.
 * @return {string} The content of the file.
 */
function include (file) {
  return HtmlService.createTemplateFromFile(file).evaluate().getContent();
}