google-apps-scriptgmail

How can I reply to the recipient of the original message within the same Gmail thread?


For an Apps Script project I need to send a reply to the recipient of the original message within the same Gmail thread. Using the available methods in the GmailApp service I couldn't find a direct way to achieve this.

I send an initial email to a recipient. I want to reply to that email (original message of the thread) and send it to the recipient of the original message within the same thread as a reminder or follow-up.

message.reply(), thread.reply() and forward() in the GmailApp service don't provide the functionality I need. The reply() method only sends the reply to the sender of the original message, while the forward() method forwards the entire thread to a new recipient.

My code:

var emailBody = " Hello, This is a reminder to the previous email" ;
var thread = GmailApp.getThreadById(existingThreadId);
var messages = thread.getMessages();
var originalMessage = messages[0];
var recipientEmail = originalMessage.getTo();// Get the recipient's email address from the original message

// Reply to the thread and send the reply to the recipient of the original email
originalMessage.reply("", { to: recipientEmail, htmlBody: emailBody });

How can I send a reply within the same Gmail thread, specifically addressing the recipient of the original message? createDraft() creates a new email separate from the original thread. In the GmailThread class in the Apps Script documentation I couldn't find a suitable method.


Solution

  • Thanks to this question I found a way:

    function sendReminder() {
      const THREAD_ID = '...';  // the thread of the original message
      const THREAD = GmailApp.getThreadById(THREAD_ID);
      const ORIGINAL_MSG = THREAD.getMessages()[0];
      // https://developers.google.com/gmail/api/reference/rest/v1/users.messages/get
      var raw = Gmail.Users.Messages.get("me", ORIGINAL_MSG.getId(), {format: "raw"}).raw;
      const ORIGINAL_TO = raw.reduce(bytesToString_, "").split("\n").filter((line) => /^To: /.test(line))[0];
      Logger.log(ORIGINAL_TO);  // the original message header "To:"
    
      const DRAFT_ID = THREAD.createDraftReply('Hello, This is a reminder to the previous email').getId();
      // https://developers.google.com/gmail/api/reference/rest/v1/users.drafts/get
      raw = Gmail.Users.Drafts.get("me", DRAFT_ID, {format: "raw"}).message.raw;
      var msg = raw.reduce(bytesToString_, ""), headerChanged = false;
      // "To:" header replacement
      msg = msg.split("\n").map(function (line) {
        if (!headerChanged && /^To: /.test(line)) {
          headerChanged = true;
          return ORIGINAL_TO;
        }
        return line;
      }).join("\n");
      Logger.log(msg);  // ready to send message (headers+body)
    
      const RESOURCE = {
        id: DRAFT_ID,
        message: {
          threadId: THREAD_ID,
          raw: Utilities.base64EncodeWebSafe(msg)
        }
      };
      // https://developers.google.com/gmail/api/reference/rest/v1/users.drafts/update
      const DRAFT = Gmail.Users.Drafts.update(RESOURCE, "me", DRAFT_ID);
      // https://developers.google.com/gmail/api/reference/rest/v1/users.drafts/send
      Gmail.Users.Drafts.send({id: DRAFT.id}, "me");
    }
    
    function bytesToString_(acc, byte) {
      return acc + String.fromCharCode(byte);
    }
    

    I used Gmail service. Method links are in code. Steps in the code are:

    1. Find the original message and get the "To:" header.
    2. Create a new draft for the same thread.
    3. Get draft message in raw format for a header replacement.
    4. Replace "To:" header by the original "To:" in the draft message.
    5. Create draft resource for updating.
    6. Update the existing draft via API.
    7. Send the updated draft.

    I used bytesToString_ to convert.