Why would some Gmail content replace correctly, while other content doesn't?

344 views
Skip to first unread message

Davis Jones

unread,
Sep 9, 2019, 7:28:10 PM9/9/19
to google-apps-sc...@googlegroups.com
Hello fellow GAS friends,

I'm building an automation for our school, and I'd like the student to receive a customized email message when they complete a form. So, I'm using the Gmail API to facilitate this. I've used this function to facilitate the automated sending of drafts in the past, but the use case this time is that I'm trying to use the draft as something of a template, with placeholder words that are replaced with the student's first name (studentName) and a unique access URL (uniqueURL).

Here's the function I've built (with the help of Romain, in the past):
function sendWelcomeEmail () {
  var ss = SpreadsheetApp.getActive().getSheetByName('Form Responses 1');
  var lastRow = ss.getLastRow();
  var studentName = ss.getRange(lastRow, 4).getValue().toString();
  var studentEmail = ss.getRange(lastRow, 6).getValue().toString();
  var accessCode = getCode();

  // get the draft by ID and its raw content
  var id = Gmail.Users.Drafts.get('me', 'r-4118928725090248526').message.id;
  //                   correct Gmail object ID ----^
  var message = GmailApp.getMessageById(id);
  var rawContent = message.getRawContent();
  
  // replaces template email with student first name and proper email address
  var updatedRawContent = rawContent.replace(/studentName/, studentName);
  updatedRawContent = updatedRawContent.replace(/newstu...@eazlonboarding.com/, studentEmail);
  updatedRawContent = updatedRawContent.replace(/uniqueURL/, accessCode);
  //   ^---- Logger.log shows that studentName and uniqueURL are *correctly* replaced
  
  // create the new message
  var message = Gmail.newMessage();
  var encodedMsg = Utilities.base64EncodeWebSafe(updatedRawContent);
  message.raw = encodedMsg;
  
  // send and log it
  Gmail.Users.Messages.send(message, "me", Utilities.newBlob(rawContent, "message/rfc822"));

};
.

The strange thing is that the raw content seems to successfully replace the studentEmail bit, but not the rest of it. When I test the function out, I get a message like this:

Screen Shot 2019-09-09 at 6.20.32 PM.png


Do you have any tips for me? Why might this be happening?

TIA!

Davis

Romain Vialard

unread,
Sep 10, 2019, 3:52:46 AM9/10/19
to Google Apps Script Community
Usually emails contain multiple parts (a plain text version of your message, the HTML version, the attachments,...) and each of those parts might be encoded, either in base64 or in QP (Quoted-Printable).
Thus, when you are trying to perform a search and replace, it won't work as you are searching for the decoded version of your marker (eg: "studentName"), currently encoded in base64 or QP.

The recipient email address is part of the email headers and most of them aren't usually encoded, hence why it has been correctly replaced.

If Logger.log() is showing that studentName & uniqueURL are correctly replaced, you might be seeing the plain text version of the email, and not the HTML version (displayed by Gmail). To check that, you can click on the three little dots next to the email you sent and click "Show original" in Gmail. Search for "Content-Type: text/plain;" and see if your markers have been correctly replaced in the plain text version.

Davis Jones

unread,
Sep 10, 2019, 2:34:35 PM9/10/19
to google-apps-sc...@googlegroups.com
You're absolutely right, Romain. I check the Original Message version, and it does show the replaced markers correctly. Do you have any tips? Should I be using a HTML templating / modifying approach to dynamically change these drafts?

--
You received this message because you are subscribed to the Google Groups "Google Apps Script Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-apps-script-c...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-apps-script-community/9382beea-1558-426e-870f-7c7c857ad048%40googlegroups.com.

Romain Vialard

unread,
Sep 11, 2019, 4:16:19 AM9/11/19
to Google Apps Script Community
First make sure you really need to use a draft as the template here. It would be much simpler to have your template as an HTML file somewhere (eg: inside your script directly).

You could also use GmailApp and the sendEmail() method to send your email, instead of using the Gmail advanced service, it would avoid having to deal with the raw content of your draft (you could simply use the getBody() method of GmailApp to retrieve the HTML content of your draft, which wouldn't be encoded, contrary to what you are currently getting with getRawContent()).

If you need to use a draft as the template and the Gmail advanced service, then you will need to:
  • Retrieve the raw content of your draft
  • Inside this raw content, retrieve the HTML part (beginning with "Content-Type: text/html;")
  • This HTML part is encoded, you will need to decode it (below "Content-Type: text/html;" you will find the encoding used, usually "Content-Transfer-Encoding: quoted-printable")
  • Then you can do your search and replace to update your markers
  • You will then need to re-encode this HTML part
  • And replace the original HTML part by this new one
  • Then you can send the email

Efficient Small Business

unread,
Sep 12, 2019, 1:59:22 PM9/12/19
to google-apps-sc...@googlegroups.com
Instead of using an email draft or having your email in the script editor, you can use a Google Doc as your template document. This makes it very simple to edit.

With the example script below, Put your variables into an object then the object keys are used as the placeholder text (w/ curly brackets added to avoid overwriting other text). If you are using a function like getRowsData (https://gist.github.com/mhawksey/51a1501493787bc5b7f1), it is very simple because you only need to make your column header match the placeholder values.

Example using a function like getRowsData:

Column title:
name
cityStateZip

Placeholder values:
{{name}}
{{cityStateZip}}

If you are manually creating the object, the object key must match the placeholder value:

Example creating your own object within the function:

Your object:
var object = {
name: "John Smith",
cityStateZip: "Denver, CO 80022"
}

Placeholder values:
{{name}}
{{cityStateZip}}

Here's the script:

function configureAndSendEmail() {

var templateDocId = “abc123”;

var subject = “Hello World”;

var rowObj = {

  firstName: “Jane”,

  lastName: “Doe”,

  email: “te...@email.com”,

  phone: “555-555-5555”,

}

  if (!templateDocId || !subject || !rowObj.email) return 'Missing values';

  var url = "https://docs.google.com/feeds/download/documents/export/Export?id=" + templateDocId + "&exportFormat=html";

  var param = {

    method: "get",

    headers: {

      "Authorization": "Bearer " + ScriptApp.getOAuthToken()

    },

    muteHttpExceptions: true,

  };

  try {

  var html = UrlFetchApp.fetch(url, param).getContentText();

  // Replace tags in template doc with object valu

  for (var [key,val] in rowObj){

    html = html.replace('{{'+key+'}}',val);

  }

       MailApp.sendEmail(rowObj.email, subject, 

                         "We're sorry. Your email does not support html", {htmlBody: html});  

    return true;

  } catch(e) {

    Logger.log(e)

    return;

  }   

}

Davis Jones

unread,
Sep 13, 2019, 1:58:39 PM9/13/19
to Google Apps Script Community
Thanks for the help, Romain and ESB.

Romain, I ended up using the getBody() method, as you suggested, to pull the HTML from a draft in the folder.

Also, I know a lot of folks have had trouble using this kind of approach with inline images. To show my thanks for the help, I'm going to post the code that works well for me if others need help with a similar use case.

function sendEmail () {
  var ss = SpreadsheetApp.getActive().getSheetByName('sheetName');
  var lastRow = ss.getLastRow();
  var studentName = ss.getRange(lastRow, 4).getValue().toString();
  var studentLastName = ss.getRange(lastRow, 5).getValue().toString();
  var studentEmail = ss.getRange(lastRow, 6).getValue().toString();
  var accessCode = getCode();
  
  // Fetch images as blobs, set names for attachments
  var welcomeBanner = UrlFetchApp
                            .fetch('googleDriveImageURL')
                            .getBlob()
                            .setName("welcomeBannerURL");
  
  // gets and modifies the template with the student's information
  var template = GmailApp.getDraft('draftID').getMessage().getBody();
  template = template.replace(/studentName/, studentName);
  template = template.replace(/uniqueURL/, 'urlHTMLCode');
  template = template.replace(/bannerHTMLcode/, '<img src="cid:welcomeBannerURL" alt="altText"></img>');
  
  // replaces template email with student first name and proper email address
  GmailApp.createDraft(
    studentEmail,
    'subjectText', 
    'plainTextVersion',
    {
      htmlBody: template,
      inlineImages:{
        welcomeBannerURL: welcomeBanner,
      },
    name: 'customName'
    }).send();
  };

Thanks again for the help!
Davis
Reply all
Reply to author
Forward
0 new messages