I am trying to access all the drafts in Gmail using Gmail Add-ons, but it logs error like this
Access denied: : Missing access token for per message scope authorization. [line: 8, function: buildAddon, file: Code]
My onTrigger function looks like this
function buildAddon(e) {
var cards = [];
var accessToken = e.messageMetadata.accessToken;
GmailApp.setCurrentMessageAccessToken(accessToken);
var draftMessages = GmailApp.getDraftMessages();
for (var i = 0; i < draftMessages.length; i++) {
cards.push(CardService.newCardBuilder()
.setHeader(CardService.newCardHeader()
.setTitle(draftMessages[i].getBody())).build());
}
return cards;
}
And the manifest json file looks like this
{
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.addons.current.message.action",
"https://www.googleapis.com/auth/gmail.addons.current.message.metadata",
"https://www.googleapis.com/auth/gmail.addons.current.message.readonly",
"https://www.googleapis.com/auth/gmail.addons.current.action.compose"
],
"gmail": {
"contextualTriggers": [
{
"unconditional": {},
"onTriggerFunction": "buildAddon"
}
],
"logoUrl": "https://www.gstatic.com/images/icons/material/system/2x/bookmark_black_24dp.png",
"name": "Draft reminder",
"version": "TRUSTED_TESTER_V2"
}
}
You’re missing the scope for drafts permission.
“https://www.googleapis.com/auth/gmail.compose
Manage drafts and send email”
Here is the reference for scopes:
https://developers.google.com/identity/protocols/googlescopes#gmailv1
Related
I am making a Google Workspace Add-on in which I want to get sheet object such as
"sheets": {
"addonHasFileScopePermission": true,
"id":"A_24Q3CDA23112312ED52",
"title":"How to get started with Sheets"
},
But I am not getting sheets object when I am trying it with docs, I am only able to get docs object.
Here is a sample event object for docs and sheets as below:
//In case of docs, I am getting docs object with details title, id etc. For docs
{ clientPlatform: 'web',
docs:
{ title: 'How to get started with Docs',
id: 'A_24Q3CDA23112312ED52sdsff',
addonHasFileScopePermission: true },
hostApp: 'docs',
userCountry: 'GB',
commonEventObject:
{ userLocale: 'en-GB',
platform: 'WEB',
hostApp: 'DOCS',
timeZone: { id: 'Asia/Kolkata', offset: 19800000 } },
userLocale: 'en',
userTimezone: { offSet: '19800000', id: 'Asia/Kolkata' } }
//But in Case of Sheets and Slides, I am not getting objects sheets or slides with details title, id etc. For Sheets
{ clientPlatform: 'web',
userLocale: 'en',
hostApp: 'sheets',
userCountry: 'GB',
userTimezone: { offSet: '19800000', id: 'Asia/Kolkata' },
commonEventObject:
{ hostApp: 'SHEETS',
platform: 'WEB',
userLocale: 'en-GB',
timeZone: { id: 'Asia/Kolkata', offset: 19800000 } } }
Here is the my add-on manifest file
{
"exceptionLogging": "STACKDRIVER",
"oauthScopes": [
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/script.locale",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/drive.file",
"https://www.googleapis.com/auth/drive.addons.metadata.readonly"
],
"runtimeVersion": "V8",
"addOns": {
"common": {
"name": "Add_on",
"logoUrl": "{my logo url}",
"layoutProperties": {
"primaryColor": "#2772ed"
},
"useLocaleFromApp": true,
"homepageTrigger": {
"runFunction": "onDriveHomePageOpen"
},
},
"drive": {
"homepageTrigger": {
"runFunction": "onDriveHomePageOpen",
"enabled": true
},
"onItemsSelectedTrigger": {
"runFunction": "onDriveItemsSelected"
}
},
"docs": {
"homepageTrigger": {
"runFunction": "onDocsHomepage"
},
"onFileScopeGrantedTrigger": {
"runFunction": "onFileScopeGrantedEditors"
}
},
"sheets": {
"homepageTrigger": {
"runFunction": "onSheetsHomepage"
},
"onFileScopeGrantedTrigger": {
"runFunction": "onFileScopeGrantedEditors"
}
},
"slides": {
"homepageTrigger": {
"runFunction": "onSlidesHomepage"
},
"onFileScopeGrantedTrigger": {
"runFunction": "onFileScopeGrantedEditors"
}
}
}
}
And Sample code is as below:
function onDocsHomepage(e) {
console.info(e);
return createAddOnView(e);
}
function onSheetsHomepage(e) {
console.info(e);
return createAddOnView(e);
}
function onSlidesHomepage(e) {
console.info(e);
return createAddOnView(e);
}
function createAddOnView(e) {
var docsEventObject;
if(e['hostApp'] == 'docs'){
docsEventObject = e['docs'];
}
if(e['hostApp'] == 'sheets'){
docsEventObject = e['sheets'];
}
if(e['hostApp'] == 'slides'){
docsEventObject = e['slides'];
}
var builder = CardService.newCardBuilder();
var cardSection = CardService.newCardSection();
if (docsEventObject!=null && docsEventObject['addonHasFileScopePermission']!=null) {
cardSection.setHeader(docsEventObject['title']);
// This add-on uses the recommended, limited-permission `drive.file`
// scope to get granular per-file access permissions.
// See: https://developers.google.com/drive/api/v2/about-auth
// If the add-on has access permission, read and display its quota.
cardSection.addWidget(
CardService.newTextParagraph().setText("File Id: " + docsEventObject['id']));
} else {
// If the add-on does not have access permission, add a button that
// allows the user to provide that permission on a per-file basis.
cardSection.addWidget(
CardService.newTextParagraph()
.setText( "The add-on needs permission to access this file." ));
var buttonAction = CardService.newAction()
.setFunctionName("onRequestFileScopeButtonClicked");
var button = CardService.newTextButton()
.setText("Request permission")
.setOnClickAction(buttonAction);
cardSection.addWidget(button);
}
return builder.addSection(cardSection).build();
}
function onRequestFileScopeButtonClicked(e) {
return CardService.newEditorFileScopeActionResponseBuilder()
.requestFileScopeForActiveDocument().build();
}
function onFileScopeGrantedEditors(e){
console.info("after granting item");
console.info(e);
return createAddOnView(e);
}
In the above code, I am only able to get docs object and not able to get sheets & slides objects after grating access of files in add-on.
As per documentation here is a sample Editor event object(for sheets): https://developers.google.com/workspace/add-ons/editors/gsao/building-editor-interfaces#event_objects
and a sample code of request file access for current document:https://developers.google.com/workspace/add-ons/editors/gsao/editor-actions#request_file_access_for_current_document
What is the issue? why I am not getting the objects for sheets and slides? Only given example works for docs not for sheets and slides.
This appears to be a bug!
I have taken the liberty of reporting this on Google's Issue Tracker for you, detailing the behaviour:
Sheets information not returned from Workspace Editor add-on event object
You can hit the ☆ next to the issue number in the top left on the page which lets Google know more people are encountering this and so it is more likely to be seen to faster.
the goal is to query fields inside a JSON object . But when executed the error above is throwned.
Dialogflow is integrated inside Slack as an App. The Google Apps Script is the web app. Here is the code:
//Writes in a sheet the Slack User id
function doPost(e) {
//Parse incoming JSON from Dialogflow into an object
var dialogflow = JSON.parse(e.postData.contents);
//Extracts userId from Slack
var desiredField = dialogflow.originalDetectIntentRequest.payload.data.event.user;
//Instantiates Sheets function
var valueRange = Sheets.newValueRange();
//Value to insert in cell
var values = [[ desiredField]];
valueRange.values = values;
//Inserts value in cell
var result = Sheets.Spreadsheets.Values.update(valueRange, 'XXX-YYY-ZZZ', 'rangeReceivingData', {valueInputOption: 'RAW'});
}
and here's the incoming JSON:
{
"responseId": "XXX-YYY-ZZZ",
"queryResult": {
"queryText": "oi",
"action": "input.welcome",
"parameters": {
},
"allRequiredParamsPresent": true,
"fulfillmentText": "Oi!",
"fulfillmentMessages": [{
"text": {
"text": ["Oi!"]
}
}],
"outputContexts": [{
"name": "projects/test-agent-xxyy/agent/sessions/xxx-yyy-zzz/contexts/__system_counters__",
"parameters": {
"no-input": 0.0,
"no-match": 0.0
}
}],
"intent": {
"name": "projects/test-agent-xxyy/agent/intents/xxx-yyy-zzz",
"displayName": "Default Welcome Intent"
},
"intentDetectionConfidence": 1.0,
"languageCode": "pt-br"
},
"originalDetectIntentRequest": {
"source": "slack",
"payload": {
"data": {
"event_time": "1589561467",
"api_app_id": "xxxyyyzzz",
"type": "event_callback",
"event": {
"event_ts": "1589561467.000200",
"team": "xxxyyyzzz",
"blocks": [{
"type": "rich_text",
"block_id": "xxxyyyzzz",
"elements": [{
"elements": [{
"text": "oi",
"type": "text"
}],
"type": "rich_text_section"
}]
}],
"ts": "1589561467.000200",
"channel_type": "im",
"client_msg_id": "xxx-yyy-zzz",
"text": "oi",
"type": "message",
"channel": "xxxyyyzzz",
"user": "T1H2E3G4O5A6L7"
},
"authed_users": ["XXXYYYZZZ"],
"event_id": "xxxyyyzzz",
"token": "xxxyyyzzz",
"team_id": "xxxyyyzzz"
}
}
},
"session": "projects/test-agent-xxyy/agent/sessions/xxx-yyy-zzz"
}
also, the fulfillment response in dialogflow have this weird formating:
fields {
key: "action"
value {
string_value: "input.welcome"
}
} ... (this is inside the sheets api error response)
When debuging it was possible to query the respondeId field (the first field from the incoming JSON). It looks that the program is taking lots of processing time on queryng further fields and in the end it throws the error. Any suggestion?
Documentation references:
Writing in Sheets
Handling POST requests in apps scripts
Dialogflow documentation on webhooks
Thanks in advance!
Error, solved. First, point:
It was possible to recover the received JSON from the webhook by using JSON.stringify:
function doPost(e) {
//Parse incoming JSON from Dialogflow
var dialogflow = JSON.parse(e.postData.contents);
//Stringify webhook after parsing
var desiredField = JSON.stringify(dialogflow);
//Outputs value in sheet
var valueRange = Sheets.newValueRange();
//Value to insert in cell
var values = [[ desiredField]];
valueRange.values = values;
//Inserts value in cell
var result = Sheets.Spreadsheets.Values.update(valueRange, 'spreadSheetId', 'range',
{valueInputOption: 'RAW'});
}
The incoming JSON was as expected then it was just a matter of querying the desired field:
from this:
//Stringify webhook after parsing
var desiredField = JSON.stringify(dialogflow);
to this:
//Extracts userId from Slack
var desiredField = dialogflow.originalDetectIntentRequest.payload.data.event.user;
Error solved!
dialogflow sends the following:
{
"responseId": "XXX-YYY-ZZZ",
"queryResult": {
"queryText": "random text",
"parameters": {
"command": "do it"
},
"allRequiredParamsPresent": true,
"fulfillmentText": "Sorry, can't understand it.",
"fulfillmentMessages": [
{
"text": {
"text": [
"Sorry, can't understand it."
]
}
}
],
"outputContexts": [
{
"name": "projects/xxx-vyyy/agent/sessions/xxx-yyy-zzz/contexts/__system_counters__",
"parameters": {
"no-input": 0,
"no-match": 0,
"command": "do it",
"command.original": "do it"
}
}
],
"intent": {
"name": "projects/xxx-vyyy/agent/sessions/xxx-yyy-zzz",
"displayName": "testCommands"
},
"intentDetectionConfidence": 1,
"languageCode": "en"
},
"originalDetectIntentRequest": {
"payload": {}
},
"session": "projects/xxx-vyyy/agent/sessions/xxx-yyy-zzz"
}
TypeError: Cannot read property 'parameters' of undefined (line 5, file "this-file")
and the goal is to query the command field. This is the current code:
function doPost(e) {
//EXTRACTION
var dialogflow = e.postData.contents;
var desiredField = dialogflow.queryResult.parameters.command;
}
but then this error thrown:
TypeError: Cannot read property 'parameters' of undefined (line 4, file "this-file")
I tried JSON.stringify(e) and e.postData.contents, but still not working.
Documentation on doPost() https://developers.google.com/apps-script/guides/web
and WebhookRequest sent by dialogflow https://cloud.google.com/dialogflow/docs/fulfillment-webhook#webhook_request
Thanks in advance!
At doPost(e), e.postData.contents is not parsed as JSON object. I think that your error message is due to this. So how about the following modification using JSON.parse()?
Modified script:
function doPost(e) {
//EXTRACTION
var dialogflow = JSON.parse(e.postData.contents); // Modified
var desiredField = dialogflow.queryResult.parameters.command;
}
Reference:
Web Apps
I have made a chrome extension to run on GMail, in which I would like to trigger a Google app Script with a button I made. Can anyone tell me how to trigger/call Google script.
This is my script:
function myFunction() {
var thread = GmailApp.getInboxThreads(0,1)[0];
var message = thread.getMessages()[0];
var sub = message.getSubject();
var body = message.getPlainBody();
var text = /(Event)|(Party)/;
result = sub.match(text);
if(result == null) {
result = body.match(text);
}
var q=0;
var labels = GmailApp.getUserLabels();
for (var i = 0; i < labels.length; i++) {
if(labels[i] === "Events") {
break;
}
else
q++;
}
if(q==labels.length)
GmailApp.createLabel("Event")
if(result != null) {
label = GmailApp.getUserLabelByName("Event");
label.addToThread(thread);
}
GMailApp.createLabel("Event");
}
My manifest.json file is below.
{
"manifest_version": 2,
"name": "drc",
"version": "0.1",
"background": {
"scripts": ["bg.js"]
},
"content_scripts": [
{
"matches": [
"https://mail.google.com/*",
"http://mail.google.com/*"
],
"js": ["content.js", "onclic.js"],
"css": ["drpdwn.css"]
}
],
"browser_action": {
"default_title": "Append Test Text"
},
"web_accessible_resources": ["logo.png", "jquery-1.10.2.min.js"],
"permissions": [
"tabs",
"activeTab",
"https://mail.google.com/*",
"http://mail.google.com/* ",
"https://www.googleapis.com/auth/gmail.*"
]
}
Now whenever a user install my extention, google script function should also be installed automatically, How do I do that.
Use the doPost() or doGet() trigger and execute your Google Apps Script.
I want to use mandrill email sending api in my google apps script. In google script I have to use JSON code but I am not getting how I will use it. I am very new in Google apps script.
var m = new mandrill.Mandrill('XXXXXXXXXXX');
var from_email = "user4#gmail.com";
var to = '[
{
"email": "recipient.email#example.com",
"name": "Recipient Name"
}
],';
// create a variable for the API call parameters
var params = {
"message": {
"from_email":from_email,
"to":[{"email":to}],
"subject": "Sending a text email from the Mandrill API",
"text": "I'm learning the Mandrill API at Codecademy, it's very difficult."
}
};
function sendTheMail() {
// Send the email!
alert('this is a mail script');
m.messages.send(params, function(res) {
log(res);
}, function(err) {
log(err);
});
}
I am not getting how to use this code in Google Apps Script.
You'd need to use the urlfetchapp.
var url = "https://mandrillapp.com/api/1.0/messages/send.json";
var your_key = "xxxxxxxxxxxxx";
var from_email = "user4#gmail.com";
var to = [{
"email": "recipient.email#example.com",
"name": "Recipient Name"
}];
var params = {
"key": your_key,
"message": {
"from_email":from_email,
"to":[{"email":to}],
"subject": "Sending a text email from the Mandrill API",
"text": "I'm learning the Mandrill API at Codecademy, it's very difficult."
}
};
var payload = JSON.stringify(params);
var options = {
'method': 'post',
'payload': payload,
'contentType' : 'application/json'
};
var response = UrlFetchApp.fetch(url, options);
Haven't tested this code, but should be something like that.
I paste sample code example to send email by Mandrill with attachment file from Google Drive.
function sendEmail() {
var MANDRILL_API_KEY = "<<your key here>>";
var files = [
"<<Google Drive File ID 1>>",
"<<Google Drive File ID 2>>",
"<<Google Drive File ID 3>>"
];
var recipients = [
{
"email": "ctrlq+to#labnol.org",
"name": "Amit Agarwal",
"type": "to"
}, {
"email": "ctrlq+cc#labnol.org",
"type": "cc"
}, {
"email": "ctrlq+bcc#gmail.com",
"type": "bcc"
}
];
var attachments = [];
for (var f in files) {
var file = DriveApp.getFileById(files[f]);
attachments.push({
"type": file.getMimeType(),
"name": file.getName(),
"content": Utilities.base64Encode(file.getBlob().getBytes())
});
}
var params = {
"key": MANDRILL_API_KEY,
"message": {
"from_email": "<<Sender's Email Address>>",
"from_name": "<<Sender Name>>",
"to": recipients,
"attachments": attachments,
"headers": {
"Reply-To": "reply#example.com"
},
"subject": "Enter email subject",
"text" : "Enter email body in plain text",
"html" : "Enter HTML content with <b>tags</b>"
}
};
var response = UrlFetchApp.fetch(
"https://mandrillapp.com/api/1.0/messages/send.json", {
'method': 'POST',
'payload': JSON.stringify(params),
'contentType': 'application/json'
});
Logger.log(response.getContentText());
}
Sample code is extracted from website ctrlq of Amit Agarwal