Creating draft email from gmail addon that extends compose UI - google-apps-script

I'm running my gmail add-on with the following scopes:
https://mail.google.com/
https://www.googleapis.com/auth/gmail.addons.execute
https://www.googleapis.com/auth/gmail.addons.current.action.compose
https://www.googleapis.com/auth/gmail.addons.current.message.metadata
https://www.googleapis.com/auth/script.external_request
I have a button, and corresponding compose action event, that is supposed to read recipient, subject, and body information from some input fields and insert this info into the compose UI.
The compose action callback looks like this:
function autofillEmailDraft(e) {
var recipient = e.formInput.recipient;
var subject = e.formInput.subject;
var body = e.formInput.body;
GmailApp.setCurrentMessageAccessToken(e.messageMetadata.accessToken);
var draft = GmailApp.createDraft(recipient, subject, body);
// Return a built draft response. This causes Gmail to present a
// compose window to the user, pre-filled with the content specified
// above.
return CardService.newComposeActionResponseBuilder()
.setGmailDraft(draft).build();
}
On clicking the button, the addon crashes with the following error message: TypeError: Cannot read property "accessToken" from undefined.
e.messageMetadata seems to be undefined. Should I be looking for the access token somewhere else?
Simply removing the offending line
GmailApp.setCurrentMessageAccessToken(e.messageMetadata.accessToken);
doesn't work. The add-on crashes with a different error
Access denied: : Missing access token for authorization. Request: MailboxService.CreateDraft.
Update after comments:
This is the code for the button widget that triggers the compose action:
var submitButton = CardService.newTextButton()
.setTextButtonStyle(CardService.TextButtonStyle.FILLED)
.setText('Yes')
.setComposeAction(
CardService.newAction().setFunctionName('autofillEmailDraft'),
CardService.ComposedEmailType.STANDALONE_DRAFT
);
This is the logged 'e' object:
{
formInput={body=Hello},
parameters={},
draftMetadata={
toRecipients=[abcd#example.com],
bccRecipients=[],
ccRecipients=[]
}
}

As this open issue indicates, filling the recipient and subject fields of an email draft from an addon that extends the compose UI is not currently supported.

Related

Disable/Remove Signature from draft created with Apps Script / Google Card Service

I'm using Google Card Service to create a button, that when clicked should create a new Gmail draft; the draft should open for editing with a pre-populated body, and without a signature.
This is my code:
function onHomepage(e) {
var builder = CardService.newCardBuilder();
var section = CardService.newCardSection()
.addWidget(CardService.newTextButton()
.setText('Button')
.setComposeAction(CardService.newAction().setFunctionName('doStuff'),
CardService.ComposedEmailType.STANDALONE_DRAFT));
builder.addSection(section);
return builder.build();
}
function doStuff(e){
var body = 'This is a <b>test</b>'
var draft = GmailApp.createDraft('','Subject','',{htmlBody: body});
return CardService.newComposeActionResponseBuilder()
.setGmailDraft(draft)
.build();
}
The draft is created and opened for editing, the only issue is that the signature of the user is appended after the html body. I've seen other questions about adding a signature to the draft; I however need to remove it.
Unfortunately this isn't currently possible.
I would suggest filing a feature request on Google's Issue Tracker detailing the behaviour you would like to see, I would file it either under the Apps Script component or the Workspace Add-ons component.

I want to setup manually the 'from' advance parameter on GmailApp object, without using getAliases()

I'm working on a Google Scrip to send an email using GmailApp with the basic parameters (recipient, subject, body). However, I want to use the advance parameter 'from', I'm collecting the email addresses on the spreadsheet (so I have the email from the sender), so I want to use that value on the 'from' advance parameter at the moment to send an email.
Any Ideas?. This is my current code:
function formSubmitReply(e) {
var recipientEmail = 'test#gmail.com';
var subjectEmail = "New User Form Request";
//Here is where I'm using the getAliases but is not working and I already have the sender's Email on the spreadsheet
var fromEmail = GmailApp.getAliases();
Logger.log(fromEmail);
//------------------------------------------------------------------------------------------------------------------
var msg = "<p>Hey IT Team</p>"+
"<p>A request has been submitted</p></br>"+
"<p>Test Field: "+e.values[0]+"</p></br>"+
"<p>Test Field 2: "+e.values[1]+"</p></br>";
if (fromEmail.length > 0){
GmailApp.sendEmail(
recipientEmail,
subjectEmail,
msg,
{
from: fromEmail[0]
});
}else{
GmailApp.sendEmail(
recipientEmail,
subjectEmail,
msg);
}
}
As of August 2020, that is not possible but you could submit a feature request through the Google Apps Script issue tracker.
Resources
https://developers.google.com/apps-script/support#missing_features
As per my testing and documentation of from parameter here, unfortunately, you cannot manually add an alias. An Alias must be one of the values returned by getAliases()
If by any chance you want to use aliases then you would have to add them in your Gmail settings first. You can check how to do that in one of the stack overflow answers here.

How do I get permission to send an email using MailApp.sendEmail from my google sheets [duplicate]

I have a problem with some Google Script stuff. Basically, my goal is to have the script check to see if a client's case was resolved and then send an email to them that the issue has been resolved. I've gotten the logic done on when to send an email, but every time I try and implement it into the spreadsheet, I get the error:
Error
You do not have permission to call MailApp.sendEmail. Required permissions: https://www.googleapis.com/auth/script.send_mail (line 8).
I've got a simple function to test the functionality of it, and when run in the script editor it works fine, but not on the spreadsheet. Here is my sample function:
function myFunction(row) {
var sheet = SpreadsheetApp.getActiveSheet();
var rng = sheet.getRange(row, 1, 1, 2);
var ara = rng.getValues();
var email = ara[0][0];
MailApp.sendEmail(email, "TEST", "This is a test of sendEmail().");
return "Email sent.";}
According to the Apps Script Custom Functions documentation:
If your custom function throws the error message You do not have permission to call X service., the service requires user authorization and thus cannot be used in a custom function.
To use a service other than those listed above, create a custom menu that runs an Apps Script function instead of writing a custom function. A function that is triggered from a menu will ask the user for authorization if necessary and can consequently use all Apps Script services.
Method 1
Basically, you can replicate the wanted behavior of the two functions above with this:
function SendEmail() {
var message = "This is your response";
var subject = "You have feed back in the parking lot";
var ss = SpreadsheetApp.getActiveSheet();
var textrange = ss.getRange("F2");
var emailAddress = ss.getRange("B2").getValue();
if (textrange.isBlank() == false)
MailApp.sendEmail(emailAddress, subject, message);
}
And in order to trigger the execution of this function, you can make use of Apps Script triggers and choose one which is the most convenient for your use-case.
Method 2
You can also create a custom menu and with the option of triggering the above function. You only need to add this:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu("My Menu")
.addItem("Send Email", "SendEmail")
.addToUi();
}
And this is how it will look like on the Spreadsheet:
Reference
Apps Script Custom Functions;
Apps Script Range Class - isBlank();
Apps Script Custom Menus;
Apps Script Triggers.
I encountered the same problem today "You do not have permission to call MailApp.sendEmail".
I solved this by doing the next steps:
open "Tools" -> "Script editor"
in "Script editor" click on "View" -> "Show manifest file"
open the "appscript.json" file that appeared in the left section of your screen and add "https://www.googleapis.com/auth/script.send_mail" to the oauthScopes, like this:
{
"oauthScopes": ["https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/script.send_mail"],
}
PS: I assigned the script to an image, which basically acts like a button.

Use Google Script's Web App as Webhook to receive Push Notification directly

My Goal: Changes in Google Drive => Push Notification to https://script.google.com/a/macros/my-domain/... => App is pushed to take action.
I don't want to setup an middle Webhook agent for receiving notification. Instead, let the Web App (by Google Script) to receive it and be pushed directly.
Since the relevant function is quite undocumented (just here: https://developers.google.com/drive/web/push) , below is the code I tried but failure.
1. Is above idea feasible??
2. My code doPost(R) seems cannot receive notification (R parameter) properly. Anyway, no response after I change the Google Drive. Any problem? (I have tried to log the input parameter R so as to see its real structure and decide if the parameter Obj for OAuth is the same as normal Drive App, but error occur before log)
function SetWatchByOnce(){
var Channel = {
'address': 'https://script.google.com/a/macros/my-domain/.../exec',
'type': 'web_hook',
'id': 'my-UUID'
};
var Result = Drive.Changes.watch(Channel);
...
}
function doPost(R) {
var SysEmail = "My Email";
MailApp.sendEmail(SysEmail, 'Testing ', 'Successfully to received Push Notification');
var Response = JSON.parse(R.parameters);
if (Response.kind == "drive#add") {
var FileId = Response.fileId;
MyFile = DriveApp.getFolderById(FileId);
...
}
}
function doGet(e) {
var HTMLToOutput;
var SysEmail = "My Email";
if (e.parameters.kind) {
//I think this part is not needed, since Push Notification by Drive is via Post, not Get. I should use onPost() to receive it. Right?
} else if (e.parameters.code) {
getAndStoreAccessToken(e.parameters.code);
HTMLToOutput = '<html><h1>App is successfully installed.</h1></html>';
} else { //we are starting from scratch or resetting
HTMLToOutput = "<html><h1>Install this App now...!</h1><a href='" + getURLForAuthorization() + "'>click here to start</a></html>";
}
return HtmlService.createHtmlOutput(HTMLToOutput);
}
....
Cloud Functions HTTP trigger(s) might also be an option ...
(which not yet existed at time of this question). this just requires setting the trigger URL as the notification URL, in the Google Drive settings - and adding some NodeJS code for the trigger; whatever it shall do. one can eg. send emails and/or FCM push notifications alike that. that trigger could also be triggered from App Script, with UrlFetchApp and there is the App Script API. one can have several triggers, which are performing different tasks (App Script is only one possibilty).
Cicada,
We have done similar functions to receive webhooks/API calls many times. Notes:
to get R, you need: var Response = R.parameters and then you can do Response.kind, Response.id, etc.
Logger will not work with doGet() and doPost(). I set it up a write to spreadsheet -- before any serious code. That way I know if it is getting triggered.

Google Apps Script - Handling Result from UrlFetchApp.fetch()

I have a spreadsheet that I only want users to modify by running a script. The script is a UiApp that has a few pre-defined input fields and text boxes and the results are submitted onto the spreadsheet. Because I only want the document modified from this app, I have to set the permissions of the spreadsheet to "Can comment." However, in doing this, the users cannot run the script (because the script edits the page and they don't have editing rights to the page). So I assume that I need to create a web app.
The web app would be stand-alone and would run as me (the owner) so that calls to the app would allow the submitted data to be written to the spreadsheet. My web app looks something like this:
function doGet() {
var app = UiApp.createApplication();
// UiApp elements are added here
return app;
}
...and the works fine when the url is accessed directly from the browser. However, I would like for the app to open w/i the spreadsheet from a spreadsheet trigger. I was thinking something like this:
var app = UrlFetchApp.fetch(url);
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.show(app);
...but this is not working. The error I get is: "Invalid argument: userInterface (line 12, file "Web App")." Line 12 is "ss.show(app)." I was hoping that the app object would be returned from UrlFetch, but I now know that an HTTPResponse is returned.
How can I convert this response into a UiApp object? Thanks.
The solution I came up with was to have the UiApp open on the spreadsheet (from a trigger) and have the user choose the drop-downs and complete the text boxes. Upon clicking the submit button, the handler function would take all of the parameters and create a payload. Then this payload was passed to a doPost(e) stand-alone web app. Because I passed the ssid, the web app was able to locate the spreadsheet/sheet/range and write/format the data in a certain way. Here is my code:
var payload = {
"ssid" : ssid,
"sheetName" : sheetName,
"row" : row,
"col" : col,
"method" : method,
"strategy" : strategy,
"summary" : summary,
};
var options = {
"method" : "post",
"payload" : payload
};
UrlFetchApp.fetch(url, options);
This way the users can input the information in a certain format without having editing rights to the sheet.