Creating a Gmail Draft with Recipients Through Gmail API

Creating a Gmail Draft with Recipients through Gmail API

'raw' should contain the entire (RFC822) email, complete with body and headers. Do not use the 'payload.headers' structure, that parsed format is only used for returning during message.get() presently.

so for 'raw' you'd want to Base64.urlsafe_encode64() a string like:
"To: someguy@example.com\r\nFrom: myself@example.com\r\nSubject: my subject\r\n\r\nBody goes here"

Creating draft via Google Gmail API

I had the same issue when I was trying to do this for the first time as well. The solution that I found was to not include the message information as part of the parameters, but rather pass that on in the :body_object as shown below.

@result = client.execute(
:api_method => gmail.users.drafts.create,
:parameters => {
'userId' => "me"
},
:body_object => {
'message' => {
'raw' => Base64.urlsafe_encode64('Test Email Message')
}
}
)

Creating an Emoji-Compatible Gmail draft with Advanced Gmail Service in Google Apps Script

After some research and learning the RFC2822 MIMEText syntax, I have the definite answer to the problem. I will answer in three parts:

  1. What Didn't Work
  2. Solution One: the Hard Way
  3. Solution Two: the New Easy Way

What Didn't Work

What doesn't work is using the actual Message object like I did in my question. Don't ask me why, it's not documented anywhere, it just doesn't work. Even if you copy all or existing parts of the JSON object from another message via Gmail.Users.Drafts.get(), GAS will still throw an error.

So what goes in is not what gets returned, even though the documentation says otherwise.

Hence the only solution is to use the raw property of the message object, which must be a base-64-encoded string in the RFC2822 format.

Solution One: the Hard Way

Combining the solutions from here and here allowed to create a basic function that generates a draft message with emojis:

function convert(toEmail, fromEmail, subject, body) {
body = Utilities.base64Encode(body, Utilities.Charset.UTF_8);
subject = Utilities.base64Encode(subject, Utilities.Charset.UTF_8);
const boundary = "boundaryboundary";
const mailData = [
"MIME-Version: 1.0",
"To: " + toEmail,
"From: " + fromEmail,
"Subject: =?utf-8?B?" + subject + "?=",
"Content-Type: multipart/alternative; boundary=" + boundary,
"",
"--" + boundary,
"Content-Type: text/plain; charset=UTF-8",
"",
body,
"",
"--" + boundary,
"Content-Type: text/html; charset=UTF-8",
"Content-Transfer-Encoding: base64",
"",
body,
"",
"--" + boundary,
].join("\r\n");
return mailData;
}

function makeApiDraft() {
const subject = "Hello MimeText World";
const body = 'This is a plain text message';
const me = Session.getActiveUser().getEmail();
const raw = convert('test@test.com', me, subject, body);
const b64 = Utilities.base64EncodeWebSafe(raw);
console.log(raw)
Gmail.Users.Drafts.create({ message: { raw: raw } }, me);
}

Nothing wrong with this solution, it works. However, if you want to go beyond this example, like adding multiple recipients, having different plain text and html bodies, managing attachments, etc., you will have to code it all by hand and that requires understanding the RFC2822 MIMEText format.

Hence enter the new easier solution.

Solution Two: the New Easy Way

I stumbled upon this library that generates MIMEText emails written in Node.js. So I thought perfect. I forked the repo and adapted a few things to make it GAS-compatible, specifically:

  1. Base 64 encoding is done with Utilities.base64Encode() and Utilities.base64EncodeWebSafe()
  2. Attaching files is done by simply passing a GAS DriveApp.File object
  3. I made sure proper MIMEText headers and base 64 encoding were present where they were needed.

And while my pull request is pending, I transpiled the whole thing with Webpack (as the library does have a dependency) and published it as a GAS library under this ID:

1HzFRRghlhuCDl0FUnuE9uKAK39GfeuUJAE3oOsjv74Qjq1UW8YEboEit

Here's an example project that you can use to test it out, but the code is basically as follows:

const testMimeText = () => {
const { message } = MimeText;
message.setSender({
name: 'Dmitry Kostyuk',
addr: 'dmitry.kostyuk@gmail.com',
});
const file = DriveApp.getFileById('1pdMwlGL1WZTbi-Q2-Fc7nBm-9NKphkKg');
const me = Session.getActiveUser().getEmail();

message.setRecipient('dmitry.kostyuk@gmail.com');
message.setSubject('Hello MimeText World!');
message.setMessage('This is a plain text message ' + getAllEmojis(), 'text/plain');
message.setMessage('<p>This is an html message</p><p>' + getAllEmojis() + '</p>\r\n\r\n', 'text/html');
message.setAttachments([file]);

const raw = message.asEncoded();
Gmail.Users.Drafts.create({ message: { raw: raw } }, me);
}

const getDriveAuth = () => DriveApp.getRootFolder();

I guess I went down a rabbit hole I never expected to, but I'm pretty happy with how it turned out :)



Related Topics



Leave a reply



Submit