can't open Slack dialog through google apps scripts - google-apps-script

I'm trying to use google apps scripts and slack to automate my work. And I wish to enter some text with the Slack dialog to modify my google spreadsheet with google apps scripts. However, with the below code, I can't open a Dialog via Slack-API's Slash command. Is my code have some problems?
function doPost(e){
var params = e.parameter;
var token = params.token;
var text = params.text;
var trigger_id = params.trigger_id;
var slackUrl = ["https://slack.com/api/dialog.open"];
if (token == "[token from slack]"){
var dialog = {
"token": "[OAuth Token]",
"trigger_id":trigger_id,
"dialog":{
"callback_id": "ryde-46e2b0",
"title": "Request a Ride",
"submit_label": "Request",
"elements": [
{
"type": "text",
"label": "Pickup Location",
"name": "loc_origin"
},
{
"type": "text",
"label": "Dropoff Location",
"name": "loc_destination"
}
]
}
};
var options = {
'method' : 'POST',
'contentType': 'application/json',
'payload' : dialog};
UrlFetchApp.fetch(slackUrl, options);
}
else{
var res = {"text":"failed token verification!"}
return ContentService.createTextOutput(JSON.stringify(res)).setMimeType(ContentService.MimeType.JSON);
}}

How about this modification?
Modification points :
Use a string to "url" of "UrlFetchApp.fetch(url, params)".
Use JSON.stringify() for dialog of the object dialog.
'contentType': 'application/json', is not required.
Modified script :
function doPost(e) {
var params = e.parameter;
var token = params.token;
var text = params.text;
var trigger_id = params.trigger_id;
var slackUrl = "https://slack.com/api/dialog.open";
if (token == "[token from slack]"){ // Please input this.
var dialog = {
"token": "[OAuth Token]", // Please input this.
"trigger_id": trigger_id,
"dialog": JSON.stringify({
"callback_id": "ryde-46e2b0",
"title": "Request a Ride",
"submit_label": "Request",
"elements": [
{
"type": "text",
"label": "Pickup Location",
"name": "loc_origin"
},
{
"type": "text",
"label": "Dropoff Location",
"name": "loc_destination"
}
]
})
}
var options = {
'method' : 'post',
'payload' : dialog,
};
UrlFetchApp.fetch(slackUrl, options);
}
else{
var res = {"text":"failed token verification!"}
return ContentService.createTextOutput(JSON.stringify(res)).setMimeType(ContentService.MimeType.JSON);
}
return ContentService.createTextOutput(); // Important
}
Note :
When there are no exceptions within the dialog submission, your app must respond with 200 OK with an empty body. This will complete the dialog.
When it uses dialog, it returns the empty body using ContentService.createTextOutput() for above, because the status code cannot be customized by Google Apps Script. When the empty body is not returned, the error occurs.
This modified script supposes that your setting for using Slack dialog has already been done.
If you modified your script, please redeploy Web Apps as a new version. By this, the script of the latest version is reflected to Web Apps.
References :
UrlFetchApp.fetch()
In my environment, I confirmed that this modified script works. But if this didn't work, I'm sorry.

Related

Update gs files from script using API

Can you please tell me if it is possible to change the contents of the gs files of the same project from the google app script. This is required to receive code updates.
I am looking for opportunities to use the API, similar to how google clasp does it.
How realistic is it to do this?
[UPDATE]
Try this code:
var scriptID = ScriptApp.getScriptId();
var url = 'https://script.googleapis.com/v1/projects/' + scriptID + '/content';
var token = ScriptApp.getOAuthToken();
var options = {
'method' : 'get',
'headers' : {'Authorization':'Bearer '+ token},
'muteHttpExceptions' : true
};
var response = UrlFetchApp.fetch(url, options);
var json = JSON.parse(response.getContentText());
Logger.log(json);
In Logger:
{error={details=[{domain=googleapis.com, #type=type.googleapis.com/google.rpc.ErrorInfo, reason=ACCESS_TOKEN_SCOPE_INSUFFICIENT, metadata={service=script.googleapis.com, method=google.apps.script.management.v1.ProjectsService.GetContent}}], message=Request had insufficient authentication scopes., code=403.0, status=PERMISSION_DENIED}}
As Tanaike mentioned, you can in fact edit gs files from Google Apps Script using the Google Apps Script API.
Actually Google Clasp does use the Google Apps Script API to make the exact same type of changes.
You can take the following request as an example using the method projects.updateContent to make a simple change in the source parameter. In addition to that, you need to make sure that the Google Apps Script API is enabled from your Apps Script Settings, and not only add the gs file you want to send in the request, but also the appsscript.json file from the manifest is required in the request.
{
"files": [
{
"name": "appsscript",
"type": "JSON",
"source": "{\n \"timeZone\": \"America/El_Salvador\",\n \"dependencies\": {},\n \"exceptionLogging\": \"STACKDRIVER\",\n \"runtimeVersion\": \"V8\"\n}"
},
{
"name": "Code",
"type": "SERVER_JS",
"source": "function myFunction() {\n Logger.log(\"This is a test\");\n}\n",
"functionSet": {
"values": [
{
"name": "myFunction"
}
]
}
}
]
}
References:
Method: projects.updateContent
Clasp

How to request specific data from the Google Search Console API using Google Apps Script?

I'm trying to recover Google Discover data from the Google Search Console through its API and display it in a Google Sheet.
I'm following a tutorial that walked me through setting up the needed authentications in the Google Cloud platform and showed me how to set up a basic request that retrieves a list of websites I have access to in the Search Console.
What I don't understand is how to modify the code below so that the API retrieves Google Discover data instead of a list of websites I have access to.
The Google Apps Script code gets the mentioned websites and populates two columns, one with the website link and another with the access level I have for that website.
function listAccountSites() {
var service = getService();
if (service.hasAccess()) {
var apiURL = "https://www.googleapis.com/webmasters/v3/sites";
var headers = {
"Authorization": "Bearer " + getService().getAccessToken()
};
var options = {
"headers": headers,
"method" : "GET",
"muteHttpExceptions": true
};
var response = UrlFetchApp.fetch(apiURL, options);
var json = JSON.parse(response.getContentText());
Logger.log(json)
var URLs = []
for (var i in json.siteEntry) {
URLs.push([json.siteEntry[i].siteUrl, json.siteEntry[i].permissionLevel]);
}
s_sites.getRange(2,1,URLs.length,2).setValues(URLs);
} else {
var authorizationUrl = service.getAuthorizationUrl();
Logger.log('Open the following URL and re-run the script: %s', authorizationUrl);
Browser.msgBox('Open the following URL and re-run the script: ' + authorizationUrl)
}
}
Using this Google documentation page I was able to construct an API request that would get the Google Discover data I need, but as I said I cannot figure out how to do this. The mentioned request gets the data I need is the following one:
{
"startDate": "2021-11-01",
"endDate": "2021-11-05",
"dimensions": [
"PAGE"
],
"type": "DISCOVER"
}
I believe your goal is as follows.
You want to achieve the following request using Google Apps Script. Ref
POST https://www.googleapis.com/webmasters/v3/sites/https%3A%2F%2Fwww.example.com%2F/searchAnalytics/query?key={MY_API_KEY}
{
"startDate": "2015-04-01",
"endDate": "2015-05-01",
"dimensions": ["country","device"]
}
In your situation, you want to use the following request body.
{
"startDate": "2021-11-01",
"endDate": "2021-11-05",
"dimensions": [
"PAGE"
],
"type": "DISCOVER"
}
If my understanding is correct, how about the following sample script?
Sample script:
This sample script supposes that your access token can be used and you have already been able to use the API. Please be careful this.
var siteUrl = "https://www.example.com/"; // Please set the site URL.
var apiURL = `https://www.googleapis.com/webmasters/v3/sites/${encodeURIComponent(siteUrl)}/searchAnalytics/query`;
var headers = { "Authorization": "Bearer " + getService().getAccessToken() };
var payload = {
"startDate": "2021-11-01",
"endDate": "2021-11-05",
"dimensions": ["PAGE"],
"type": "DISCOVER"
};
var options = {
"headers": headers,
"method": "POST",
"muteHttpExceptions": true,
"contentType": "application/json",
"payload": JSON.stringify(payload),
};
var response = UrlFetchApp.fetch(apiURL, options);
References:
Search Analytics: query
fetch(url, params)

How to filter Google Drive API v3 mimeType?

I wrote a script that uses DriveApp to read Google Drive, SpreadsheetApp to log data in Sheets, and Google Drive API v3 + service account + OAuth to make changes on behalf of G Suite users.
It would be nice to search Google Drive from the target user perspective (calling Google Drive API v3) instead of the account running the script (calling DriveApp). I can't get the filter to work.
The query is built with parent folder keys, mimeType = or mimeType != for folders, and is passed into the function. The format is:
var query = "('GoogleDriveFolderKey01' in parents or 'GoogleDriveFolderKey02' in parents) and trashed = false and mimeType = 'application/vnd.google-apps.folder'"
The DriveApp function uses:
files = Drive.Files.list({
q: query,
maxResults: 100,
pageToken: pageToken
});
The Google Drive API v3 function uses:
var url = 'https://www.googleapis.com/drive/v3/files/'
var options = {
'contentType': 'application/json',
'method' : 'get',
'headers' : { Authorization: 'Bearer ' + service.getAccessToken() },
'muteHttpExceptions': true,
'corpora' : 'domain',
'q' : query,
'spaces' : 'drive',
'pageSize' : 100,
'pageToken' : pageToken
};
var response = UrlFetchApp.fetch(url, options);
var resultParsed = JSON.parse(response.getContentText());
files = resultParsed.files
pageToken = resultParsed.pageToken
The results with DriveApp are as expected, but Google Drive API v3 results in:
"files": [
{
"kind": "drive#file",
"id": "01abc123_etc",
"name": "Something something (2021-04-15).pdf",
"mimeType": "application/pdf"
},
{
"kind": "drive#file",
"id": "02ABC4321-qwertyuiop",
"name": "Super Special Worksheet",
"mimeType": "application/vnd.google-apps.spreadsheet"
},
{
"kind": "drive#file",
"id": "whatever",
"name": "Copy of V1",
"mimeType": "application/vnd.google-apps.folder"
},
...
Any ideas?
Edit:
Thank you! The problem seems to be 'corpus' and 'space'. I have used to following in Google AppsScript:
var options = {
'contentType': 'application/json',
'method' : 'get',
'headers' : { Authorization: 'Bearer ' + service.getAccessToken() },
'muteHttpExceptions': true,
'pageSize' : 100
};
url += '?q=' + encodeURIComponent(query);
if ( pageToken.length > 0 ) url += '&pageToken=' + pageToken;
var response = UrlFetchApp.fetch(url, options);
How about this answer?
Modification points:
At the method of "Files: list" in Drive API v3, the values of corpora, q, space, pageSize, pageToken are used as the query parameters. I think that the reason of your issue is due to this.
At GET method, contentType is not required.
Modified script:
When your script is modified, it becomes as follows.
var query = "('GoogleDriveFolderKey01' in parents or 'GoogleDriveFolderKey02' in parents) and trashed = false and mimeType = 'application/vnd.google-apps.folder'"
var url = 'https://www.googleapis.com/drive/v3/files' // Modified
var options = { // Modified
'method' : 'get',
'headers' : { Authorization: 'Bearer ' + service.getAccessToken() },
'muteHttpExceptions': true,
};
url += `?corpora=domain&q=${encodeURIComponent(query)}&spaces=drive&pageSize=100&pageToken=${pageToken}`; // Added
var response = UrlFetchApp.fetch(url, options);
var resultParsed = JSON.parse(response.getContentText());
files = resultParsed.files
pageToken = resultParsed.pageToken
Note:
This modified script supposes that service.getAccessToken() can be used for using the method of "Files: list" in Drive API v3.
If an error occurs please remove corpora=domain from the URL like below and test it again.
url += `?q=${encodeURIComponent(query)}&spaces=drive&pageSize=100&pageToken=${pageToken}`;
Reference:
Files: list

How to create a spreadsheet with an associated script?

I'd like to programatically create a batch of spreadsheets which contain different data, but all of which contain a button that is associated with a custom backend function.
For example, each spreadsheet should have a button that, when pressed exports the data to another sheet.
Is such a thing possible?
One idea I had was maybe to create a template that includes the button and associated Apps Script and then make a copy of that spreadsheet and fill it with the custom data.
The Apps-Script API allows you to programmatically create Apps Script projects with the option of binding them to a Google Sheet
Convert your project into a Cloud Platform project and enable Apps Script API for this project
Give your projects the necessary scopes in the manifest file
Incorporate Apps Script API into Apps Script with a Urlfetch request
Create a new Apps Script Project with Method: projects.create specifying the parentId
Add contents to the project with Method: projects.updateContent. You can store the contents in a variable and thus add the same content to all of your Apps Script projects.
Sample:
JSON file
{
"timeZone": "America/New_York",
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/script.projects",
"https://www.googleapis.com/auth/script.external_request"
],
"dependencies": {
},
"exceptionLogging": "STACKDRIVER"
}
.gs file
function createSpreadsheetwithScript() {
var ss=SpreadsheetApp.create('mySpreadsheet');
var id=ss.getId();
var token = ScriptApp.getOAuthToken();
var url = "https://script.googleapis.com/v1/projects";
var payload = {
"title": "myAutoCreatedScript",
"parentId": id
}
var options = {
"method" : "POST",
"muteHttpExceptions": true,
"headers": {
'Authorization': 'Bearer ' + token
},
"contentType": "application/json",
"payload": JSON.stringify(payload)
}
var response = UrlFetchApp.fetch(url,options);
var scriptId=JSON.parse(response).scriptId;
var url2="https://script.googleapis.com/v1/projects/"+scriptId+"/content";
//test content
var source="function myFunction() {\n var x=1;\n}";
var JSONsource="{\"timeZone\":\"America/New_York\",\"exceptionLogging\":\"STACKDRIVER\"}";
var payload2 = {
"files": [
{
"name": "this is the gs. file",
"type": "SERVER_JS",
"source": source
},
{
"name": "appsscript",
"type": "JSON",
"source": JSONsource,
"updateTime":"2018-03-04T19:49:08.871Z",
"functionSet":{
"values":[{"name":"myFunction"}]}
}
]
}
var options2 = {
"headers": {
'Authorization': 'Bearer ' + token,
},
"contentType": "application/vnd.google-apps.script+json",
"method" : "PUT",
"payload": JSON.stringify(payload2)
}
var response2 = UrlFetchApp.fetch(url2,options2);
}
Make sure to enable the Apps-script API before using it under https://script.google.com/home/usersettings and that your upadteContent request inclusdes a manifest file
More samples
To write a custom function:
Create or open a spreadsheet in Google Sheets.
Select the menu item Tools > Script editor. If you are presented with a welcome screen, click Blank Project on the left to start a new project.
Delete any code in the script editor....
Select the menu item File > Save....
All done!

Google Spreadsheet to pdf with watermark in Google script

I want to convert the spreadsheet with a watermark/background image, and send + save the generated pdf
The converting to pdf worked, but I don't know if/how you can put an image to the generated pdf.
This is what i got now:
function ExportAndSent(subject, filename, email) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var message = "A message";
var tempSpreadsheet = SpreadsheetApp.create(filename);
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet = ss.getActiveSheet();
sheet.copyTo(tempSpreadsheet);
tempSpreadsheet.deleteActiveSheet();
var pdf = DriveApp.getFileById(tempSpreadsheet.getId()).getAs(MimeType.PDF);
var pdfBytes = pdf.getBytes();
var attach = {fileName:(filename + ".pdf"),content:pdfBytes, mimeType:MimeType.PDF};
// Here we need to put a watermark
// Send and export
MailApp.sendEmail(email, subject, message, {attachments:[attach]});
DriveApp.createFile(pdf);
// Delete Temporary
DriveApp.getFileById(tempSpreadsheet.getId()).setTrashed(true);
}
It looks like the spreadsheet is already converted to a PDF. A third party service can be used to add a watermark to open the PDF and add a watermark to it. PDF WebAPI is a free service you can use. Below I put together a function that can take in a PDF and add a watermark to it. The watermark is hardcoded to "watermark.jpg" currently, but that can be changed. From looking at the code above, the function should be called between the following lines:
var pdf = DriveApp.getFileById(tempSpreadsheet.getId()).getAs(MimeType.PDF);
var pdfBytes = pdf.getBytes();
The "pdf" variable should be used as input, and "pdfBytes", can take the return value of appendWatermark().
You will need to sign up for a free PDF WebAPI account to get an ID and key.
function appendWatermark(inputPDF) {
var fileBlob = inputPDF.copyBlob();
var decorationData = [{
"watermarkSettings": {
"opacity": 50,
"source": {
"file": "watermark.jpg",
"page": 1
},
"scale": {
"type": "relative",
"percent": 90
},
"location": "top",
"rotation": "0"
}
}];
var applicationData = {
"id": "",
"key": ""
};
var payload = {
"application": JSON.stringify(applicationData),
"input": fileBlob,
"decorationData": JSON.stringify(decorationData),
"resource": DriveApp.getFilesByName("watermark.jpg").next().getBlob()
};
var options = {
"method": "post",
"payload": payload
};
var response = UrlFetchApp.fetch("https://pdfprocess.datalogics.com/api/actions/decorate/document", options);
return response.getBytes;
}