Blog

Create an email auto-responder with Apps Script

today November 18, 2022

Having a system that automatically responds to incoming email messages can be a great time saver. In this post you will learn how to create such a system for Gmail, using Google Apps Script.

Business requirements

When designing the solution, I've had the following business requirements:

  • Create a trigger to have the script run automatically every minute
  • Make it easy to define what kinds of emails to respond to
  • Ensure that emails don't get duplicate replies even if they remain in the inbox
  • Log how many emails received a reply in every run
Interested in customizing this script? Contact me

The script

You can download the complete script from here. For those who want more information, I will go through the code below.

The first thing I like to do is to create a global config object that is used to keep some definitions that are used throughout the script. I place the object at the top of the file, which will make it easy for you to find the definitions and change them as needed.

// Manage script configuration here
const g = {
  subjectFilter: 'New inquiry',
  labelName: 'Auto-replied',
  textBody: `Hi,

  Thank you for contacting us. This is an auto-response system. 
  One of our agents will contact you shortly.`,
  htmlBody: `<p>Hi,</p>

  <p>Thank you for contacting us. This is an auto-response system. 
  One of our agents will contact you shortly.</p>`,
  maxThreads: 100,
  startingThread: 0,
  replyCount: 0,
};

g is the global object. subjectFilter contains the subject keywords that the script should respond to. In other words, the script will reply only to emails whose subject line contains these keywords.

labelName is the Gmail label that the script assigns to the replied email. We use the label name alongside subjectFilter to focus only on new emails. This will prevent sending duplicate replies to each email.

textBody and htmlBody contain the texts that the script will inject into the reply.

The next two keys have to do with handling many threads. Google Apps Script has a limit on the number of email threads that can be fetched at one time. If we have a lot of threads then we need to pull them in several requests, by paging through them, like paging through search results. maxThreads specifies how many threads to pull at one time, and startingThread is akin to a page number: fetch from thread zero, then from thread one hundred, and so on.

Lastly, replyCount sums up the number of emails that receive a reply.

Initializing the automation

Every time the script runs, it performs an initialization for the entire automation. Here's the code for that:

function init_() {
  const labels = GmailApp.getUserLabels().map((l) => l.getName());
  if (!labels.includes(g.labelName)) {
    GmailApp.createLabel(g.labelName);
  }
  g.label = GmailApp.getUserLabelByName(g.labelName);
  const queryFilter = {
    subject: g.subjectFilter,
    '-label': g.labelName,
  };
  g.query = Object.entries(queryFilter)
    .map((e) => e.join(':'))
    .join(' ');
  const triggers = ScriptApp.getProjectTriggers();
  if (triggers.length == 0) {
    ScriptApp.newTrigger('checkAndReply')
      .timeBased().everyMinutes(1).create();
  }
}

First, the script checks if Gmail already has a user-generated label of the name defined in g.labelName. If it doesn't then it creates a label, and stores a reference to it in g.label.

Next, we define a filter to limit the scope of the threads we want Gmail to search for. We want threads with subject lines that contain our key words, as well as threads that haven't been labeled with our label. g.query takes the keys and values of the queryFilter object and joins them into a string that GmailApp.search can use.

Finally, we want this script to run automatically every minute. To do so, we check whether the script already has a trigger (from a prior run), and if not, then we create one.

Auto responder trigger

The main body

Now we get to the main part of the automation: checking for new emails, replying to them, and labeling them to avoid duplicate replies.

First, we declare the main function that the trigger will run and execute the initialization.

function checkAndReply(){
  init_();
}

Next, we create a loop to iterate through the threads. We ask GmailApp to search for threads that meet our filtering criteria, starting at a specific thread number and retrieving our maximum threads threshold.

We increment our starting thread number by the maximum threads, and we check if the number of returned threads is less than our maximum threads count. If it is then that's our sign that there are no more threads to page through and we break out of the loop.

function checkAndReply() {
  init_();
  while (true) {
    const threads = GmailApp.search(g.query, g.startingThread, g.maxThreads);
    g.startingThread += g.maxThreads;
    if (threads.length < g.maxThreads) {
      break;
    }
  }
}

Next, we handle each of the returned threads separately. We reply to it using our text strings, and then label the thread. We also increment the count of emails we've replied to.

threads.forEach((thread) => {
  thread.reply(g.textBody, { htmlBody: g.htmlBody });
  g.label.addToThread(thread);
  g.replyCount++;
});

The final piece is to log out the number of emails replies we sent this time around:

const text =
  g.replyCount == 0
    ? 'No new emails found.'
    : g.replyCount == 1
    ? 'Replied to one new email.'
    : `Replied to ${g.replyCount} new emails.`;
console.log(text);

Further customization

Again, you can download the complete script from here. The script can be easily modified to handle more complex logic. You could, for instance:

  • Create an out-of-office responder for nights and weekends
  • Use different reply texts for different emails based on some business criteria, such as subject keywords or sender information
  • Store the text in Google Docs so that non-developers can modify the replies without handling the code
  • Log the transactions to a Google Sheet for more in-depth analysis
Interested in customizing this script? Contact me