3

I get a lot of system alert-related emails with empty bodies. The server has scripts that send emails with populated subjects but the body is only populated if there is an issue to report which is rare.

In Gmail how do I filter for these messages? While I know how to filter using the "From" and "To" fields, I don't know how to make a filter check if the body is populated.

0

2 Answers 2

1

Label Gmail Messages w/out Body

  1. Script will search for Gmail messages that have an empty body.
  2. Folder to search can be specified (e.g. Inbox, Sent, etc.)
  3. Script will apply 1 of 2 tracking labels. My example labels start with a _ so Gmail sorts them above other labels:
    • Label 1 => For messages that have been checked that have a body (eg. _checked). Labeling them avoids rechecking them.
    • Label 2 => Messages with no body (eg. _empty)
  4. Optionally specify if emails with attachments can be considered empty using isAttachEmpty

Functions

There are 4 functions:

  1. findEmptyMsgBodies() is the main function.

    • You can specify your batch size (do not exceed 500), and the number of batches per script run. For example,
      batchesPerRun:4 x msgsPerBatch:500 = 2,000 messages per execution.      
      
    • Script averages ~1 minute per batch of 500, or ~4 minutes for 2,000 messages so you can set a time-based trigger for every 5 minutes and have some buffer. The script will be faster if manually run but who wants to keep doing that every 3 minutes?
    • If you are processing a lot of messages you may hit your daily quota limits and have to wait 24 hours for a reset.
  2. getLabelIds() is a function that should be run the first time you set up the script as well as any time you change your tagging labels. Its sole purpose is to create the user labels (if they don't already exist) and, regardless if you manually created the labels or used the script, this function will retrieve the label IDs required by the main function findEmptyMsgBodies() so they can be manually entered in track[].id.
    track.id waiting to be populated.

  3. install_trigger() can be used to create a time-based trigger or you can do this manually

  4. delete_triggers() will delete any existing triggers, or you can do that manually.

Script

/**
 *   +----------------------------------+
 *   |  Label Gmail Messages w/out Body | 
 *   +----------------------------------+ 
 *
 *   Requires installing Gmail API v1 service in project
 */

function findEmptyMsgBodies() { 
  const track = [
    { name: "_checked",  id: "" },
    { name: "_empty",    id: "" }
  ];
  const msgsPerBatch = 500;
  const batchesPerRun = 4;
  const isAttachEmpty = 0;  // (1 or 0): Specify if empty body can include an attachment

  // Be careful changing anything  below this line
  // ---------------------------------------------

  let counter = 0;
  const query = (!isAttachEmpty ? "\-has:attachment " : "") +
    "in:sent \-label:({" + [track[0].name, track[1].name].join(" ") + "})"; 

  listMessages();

  function listMessages(token) {
    counter += 1;
    console.log("Batch", counter,"of", batchesPerRun, "starting");
    const list = Gmail.Users.Messages.list("me", {
      q: query, 
      pageToken: token,
      maxResults: msgsPerBatch 
    });
    token = list.nextPageToken;
    const messages = list.messages;

    if (messages) {
      for (let i = 0; i < messages.length; i++) {
        let msgId = messages[i].id;
        let snip = Gmail.Users.Messages.get("me", msgId,{format: "minimal"}).snippet;
        track.forEach(t => t.msgs = []); 
        (!snip) ? track[1].msgs.push(msgId) : track[0].msgs.push(msgId);      
      }
      track.forEach(t => (t.msgs.length > 0) &&
        Gmail.Users.Messages.batchModify({
          ids: t.msgs, 
          addLabelIds: t.id }, "me"));
    }
    while ((token) && (counter < batchesPerRun)) listMessages(token);
  }
  console.log(counter,"batches (of", batchesPerRun,") completed.");
}

function install_trigger(){ 
  delete_triggers()   // avoids duplicates
  ScriptApp.newTrigger("findEmptyMsgBodies").timeBased().everyMinutes(5).create();
}

function delete_triggers(){
  ScriptApp.getProjectTriggers().forEach(t => ScriptApp.deleteTrigger(t));
}

function getLabelIds() {   // retrieves label IDs for you to manually enter in 'track.Id'
  const ids = [], names = [], output = []; 
  track.forEach(t => GmailApp.getUserLabelByName(t.name) || GmailApp.createLabel(t.name));
  const labels = Gmail.Users.Labels.list('me').labels;
  track.forEach(t => labels.forEach(l => (l.name == t.name) && ids.push(l.id) && names.push(l.name)));
  const padnm = getLongest(names)+3, pdid = getLongest(ids)+2;
  [0,1].forEach(n => output.push(" { name: \""+(names[n] + "\",").padEnd(padnm)+"id: \""+(ids[n]+"\"").padEnd(pdid)+"}"));
  console.log("Copy the entire constant declaration below, starting before\n"+
      "\"const track = [\" and up to and including the closing \"];\" on\n"+
      "the 4th line of code, and then paste all of it over the\n"+
      "existing \"track\" constant declared at the top of the script\n"+("↓").padEnd(15)+("↓").padEnd(15)+"↓");
  console.log("const track = [\n",output.join(",\n "), "\n];");

  function getLongest(array) {
      return testvalue = array.reduce((a, b) => 
          a.length > b.length ? a.length : b.length,);
  }
}
-1

All of the available search operators are listed in GMail advanced search help. There's no standard wildcard selector to put into Doesn't have but according to a question about wildcards you can use Perl RegEx for query. This way, you need to construct a query that searches for the absence of any character in the body.

enter image description here

enter image description here

4
  • And how do you check if the body of the mail is empty?
    – Alex
    Jun 25, 2012 at 13:00
  • You check with the parameters that aren't empty, such as From and Subject.
    – dnbrv
    Jun 25, 2012 at 13:07
  • 1
    The OP stated that the script sends empty emails, so how do you check for this situation—if an email’s body is empty or not? That is the OP’s question.
    – Alex
    Jun 25, 2012 at 13:11
  • @Alex: See the edits.
    – dnbrv
    Jun 25, 2012 at 13:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.