Slack Webhooks Connected to Google Sheets Get Next Row - google-apps-script

I have this code that works great for using an outgoing webhook in slack to fill in a google sheet, and then bounce back a formatted response from the google sheet into a slack channel, but I can't figure out how to get it to pull any other columns in the google sheet. Here is the google sheet link. So it goes as follows:
In a slack chanel you can use the outgoing webhook and post"nextrow;test;test;test
This information is filled into the google sheet with a new row
google script formats this info into a payload and posts a formatted version of the info into the slack channel
This all occurs in columns A:F and in row G there is an array formula and I would like for the google script to pull that columns value in that new row and post it back in the slack response. I tried entering in sheets.getRangeByName('test').getValue(nR,1)
but that didn't work, and I also tried sheet.getRange(noteTakerCell).getValue() but that also didn't work and it also seems to keep the whole thing from working anymore. Here is an example of the response posted back in Slack, and I would like this to include the test column new row.
Here is the code that currently works for columns A:F, I removed script I wrote for trying to get column G new row since it seems to stop everything from working. Any help would be greatly appreciated. Thanks!
function doPost(req) {
var sheets = SpreadsheetApp.openById('1P4goTvi2a7yjh-fBccRJPJ9ZFNly8OhxmABXkuhfbBQ');
var params = req.parameters;
var nR = getNextRow(sheets) + 1;
if (params.token == "[Slack Outgoing Webhook]") {
// PROCESS TEXT FROM MESSAGE
var textRaw = String(params.text).replace(/^\s*update\s*:*\s*/gi,'');
var text = textRaw.split(/\s*;\s*/g);
// FALL BACK TO DEFAULT TEXT IF NO UPDATE PROVIDED
var project = text[0] || "No Project Specified";
var yesterday = text[1] || "No update provided";
var today = text[2] || "No update provided";
var blockers = text[3] || "No update provided";
// RECORD TIMESTAMP AND USER NAME IN SPREADSHEET
sheets.getRangeByName('timestamp').getCell(nR,1).setValue(new Date());
sheets.getRangeByName('user').getCell(nR,1).setValue(params.user_name);
// RECORD UPDATE INFORMATION INTO SPREADSHEET
sheets.getRangeByName('project').getCell(nR,1).setValue(project);
sheets.getRangeByName('yesterday').getCell(nR,1).setValue(yesterday);
sheets.getRangeByName('today').getCell(nR,1).setValue(today);
sheets.getRangeByName('blockers').getCell(nR,1).setValue(blockers);
var channel = "[Slack Channel]";
postResponse(channel,params.channel_name,project,params.user_name,yesterday,today,blockers);
} else {
return;
}
}
function getNextRow(sheets) {
var timestamps = sheets.getRangeByName("timestamp").getValues();
for (i in timestamps) {
if(timestamps[i][0] == "") {
return Number(i);
break;
}
}
}
function postResponse(channel, srcChannel, project, userName, yesterday, today, blockers) {
var payload = {
"channel": "#" + channel,
"username": "New Update",
"icon_emoji": ":white_check_mark:",
"link_names": 1,
"attachments":[
{
"fallback": "This is an update from a Slackbot integrated into your organization. Your client chose not to show the attachment.",
"pretext": "*" + project + "* posted an update for stand-up. (Posted by #" + userName + " in #" + srcChannel + ")",
"mrkdwn_in": ["pretext"],
"color": "#D00000",
"fields":[
{
"title":"Yesterday",
"value": yesterday,
"short":false
},
{
"title":"Today",
"value": today,
"short":false
},
{
"title":"Blockers",
"value": blockers,
"short": false
}
]
}
]
};
var url = '[Slack Incoming Webhook]';
var options = {
'method': 'post',
'payload': JSON.stringify(payload)
};
var response = UrlFetchApp.fetch(url,options);
}

Related

Google Sheet Script not triggered when sheet is edited by service account?

So I have written a gs function that sends an email when someone inserts a new row in the sheet. It works fine when users manually insert data, however that sheet is also used by a service account which inserts a new row through the API, and the edit event is not triggered in that case.
This is the trigger that I'm using
I created the script and the trigger as the owner of the sheet, but that didn't fix anything, so I'm out of ideas.
The only way to trigger a user event with code is to use the Sheets API, with a special setting to set the value as USER_ENTERED And it only works with the "On Change" event. So, you'll need to create a second trigger for "On Change" but you can use the same function name. Although you may need to modify the function to deal with a different event object. Or you could use a different function.
So, your service account will need to run code that uses the Sheets API to set values in your Google Sheet.
You can use either the REST API or the Sheets Advanced Service.
To use the Advance Sheets Service the code would look like the following:
function writeToSheet() {
id = "Put the Sheet ID here";
var rowValues = [
["one","two"],
];
var request = {
'valueInputOption': 'USER_ENTERED',
'data': [
{
"range": "Sheet1!A2:B2",
"majorDimension": "ROWS",
"values": rowValues,
},
],
};
var response = Sheets.Spreadsheets.Values.batchUpdate(request, id);
Logger.log('response ' + JSON.stringify(response))
}
For the REST API the basic code is as follows:
function writeToSheet() {
var id,options,range,response,sh,ss,url,values;
id = 'Put the spreadsheet ID here';
range = "Sheet1!A1:A1";
values = {values: [['3','two','nine']]}; // Modified
url = "https://sheets.googleapis.com/v4/spreadsheets/" +
id + "/values/" + range + ":append?valueInputOption=USER_ENTERED";
options = {
"method":"post",
"muteHttpExceptions": true,
"headers": {
"Authorization": "Bearer " + ScriptApp.getOAuthToken()
},
"contentType": "application/json", // Added
"payload": JSON.stringify(values) // Added
}
response = UrlFetchApp.fetch(url,options)
response = JSON.parse(response);
//Logger.log('response ' + JSON.stringify(response))
}

How to use Google Photos API Method: mediaItems.search in Google apps script for a spreadsheet

I really tried to figure this out on my own...
I am trying to load photo metadata from google photos into a sheet using the Google Photos API and google apps script.
I was able to make some progress after a lot of help on a previous question
Is it possible to load google photos metadata into google sheets?
I now have two functions.
function photoAPI_ListPhotos() - Uses Method: mediaItems.list and gives me all my photos that are not archived
function photoAPI_ListAlbums() - Uses Method: albums.list and gives me all my albums
What I want to do is retrieve all photos from a specific album. Method: mediaItems.search should do this but it uses the POST protocol and the previous working examples I found only use GET. Looking at the examples available on that page, there is a javascript portion but it does not work in apps script.
The documentation for UrlFetchApp tells me how to format a POST request but not how to add the parameters for authentication.
The external APIs also is not giving me the examples I am looking for.
I feel like I'm missing some essential tiny piece of info and I hope I'm not wasting everyone's time asking it here. Just a solid example of how to use POST with oauth in apps script should get me where I need to go.
Here is my working function for listing all non-archived photos.
function photoAPI_ListPhotos() {
/*
This function retrieves all photos from your personal google photos account and lists each one with the Filename, Caption, Create time (formatted for Sheet), Width, Height, and URL in a new sheet.
it will not include archived photos which can be confusing if you happen to have a large chunk of archived photos some pages may return only a next page token with no media items.
Requires Oauth scopes. Add the below line to appsscript.json
"oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/photoslibrary", "https://www.googleapis.com/auth/photoslibrary.readonly", "https://www.googleapis.com/auth/script.external_request"]
Also requires a standard GCP project with the appropriate Photo APIs enabled.
https://developers.google.com/apps-script/guides/cloud-platform-projects
*/
//Get the spreadsheet object
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Check for presence of target sheet, if it does not exist, create one.
var photos_sh = ss.getSheetByName("photos") || ss.insertSheet("photos", ss.getSheets().length);
//Make sure the target sheet is empty
photos_sh.clear();
var narray = [];
//Build the request string. Max page size is 100. set to max for speed.
var api = "https://photoslibrary.googleapis.com/v1/mediaItems?pageSize=100";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };
//This variable is used if you want to resume the scrape at some page other than the start. This is needed if you have more than 40,000 photos.
//Uncomment the line below and add the next page token for where you want to start in the quotes.
//var nexttoken="";
var param= "", nexttoken;
//Start counting how many pages have been processed.
var pagecount=0;
//Make the first row a title row
var data = [
"Filename",
"description",
"Create Time",
"Width",
"Height",
"ID",
"URL",
"NextPage"
];
narray.push(data);
//Loop through JSON results until a nextPageToken is not returned indicating end of data
do {
//If there is a nextpagetoken, add it to the end of the request string
if (nexttoken)
param = "&pageToken=" + nexttoken;
//Get data and load it into a JSON object
var response = UrlFetchApp.fetch(api + param, options);
var json = JSON.parse(response.getContentText());
//Check if there are mediaItems to process.
if (typeof json.mediaItems === 'undefined') {
//If there are no mediaItems, Add a blank line in the sheet with the returned nextpagetoken
//var data = ["","","","","","","",json.nextPageToken];
//narray.push(data);
} else {
//Loop through the JSON object adding desired data to the spreadsheet.
json.mediaItems.forEach(function (MediaItem) {
//Check if the mediaitem has a description (caption) and make that cell blank if it is not present.
if(typeof MediaItem.description === 'undefined') {
var description = "";
} else {
var description = MediaItem.description;
}
//Format the create date as appropriate for spreadsheets.
var d = new Date(MediaItem.mediaMetadata.creationTime);
var data = [
MediaItem.filename,
"'"+description, //The prepended apostrophe makes captions that are dates or numbers save in the sheet as a string.
d,
MediaItem.mediaMetadata.width,
MediaItem.mediaMetadata.height,
MediaItem.id,
MediaItem.productUrl,
json.nextPageToken
];
narray.push(data);
});
}
//Get the nextPageToken
nexttoken = json.nextPageToken;
pagecount++;
//Continue if the nextPageToaken is not null
//Also stop if you reach 400 pages processed, this prevents the script from timing out. You will need to resume manually using the nexttoken variable above.
} while (pagecount<4 && nexttoken);
//Continue if the nextPageToaken is not null (This is commented out as an alternative and can be used if you have a small enough collection it will not time out.)
//} while (nexttoken);
//Save all the data to the spreadsheet.
photos_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
You want to retrieve all photos of the specific album using Google Photo API.
You want to know how to use the method of mediaItems.search using Google Apps Script.
You have already been able to retrieve the data using Google Photo API.
If my understanding is correct, how about this sample script? Please think of this as just one of several answers.
Sample script 1:
var albumId = "###"; // Please set the album ID.
var headers = {"Authorization": "Bearer " + ScriptApp.getOAuthToken()};
var url = "https://photoslibrary.googleapis.com/v1/mediaItems:search";
var mediaItems = [];
var pageToken = "";
do {
var params = {
method: "post",
headers: headers,
contentType: "application/json",
payload: JSON.stringify({albumId: albumId, pageSize: 100, pageToken: pageToken}),
}
var res = UrlFetchApp.fetch(url, params);
var obj = JSON.parse(res.getContentText());
Array.prototype.push.apply(mediaItems, obj.mediaItems);
pageToken = obj.nextPageToken || "";
} while (pageToken);
Logger.log(mediaItems)
At the method of mediaItems.search, albumId, pageSize and pageToken are included in the payload, and the values are sent as the content type of application/json.
Sample script 2:
When your script is modified, how about the following modified script?
function photoAPI_ListPhotos() {
var albumId = "###"; // Please set the album ID.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var photos_sh = ss.getSheetByName("photos") || ss.insertSheet("photos", ss.getSheets().length);
photos_sh.clear();
var narray = [];
var api = "https://photoslibrary.googleapis.com/v1/mediaItems:search";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var nexttoken = "";
var pagecount = 0;
var data = ["Filename","description","Create Time","Width","Height","ID","URL","NextPage"];
narray.push(data);
do {
var options = {
method: "post",
headers: headers,
contentType: "application/json",
payload: JSON.stringify({albumId: albumId, pageSize: 100, pageToken: nexttoken}),
}
var response = UrlFetchApp.fetch(api, options);
var json = JSON.parse(response.getContentText());
if (typeof json.mediaItems === 'undefined') {
//If there are no mediaItems, Add a blank line in the sheet with the returned nextpagetoken
//var data = ["","","","","","","",json.nextPageToken];
//narray.push(data);
} else {
json.mediaItems.forEach(function (MediaItem) {
if(typeof MediaItem.description === 'undefined') {
var description = "";
} else {
var description = MediaItem.description;
}
var d = new Date(MediaItem.mediaMetadata.creationTime);
var data = [
MediaItem.filename,
"'"+description,
d,
MediaItem.mediaMetadata.width,
MediaItem.mediaMetadata.height,
MediaItem.id,
MediaItem.productUrl,
json.nextPageToken
];
narray.push(data);
});
}
nexttoken = json.nextPageToken || "";
pagecount++;
} while (pagecount<4 && nexttoken);
photos_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
Note:
This script supposes as follows.
Google Photo API is enabed.
The scope of https://www.googleapis.com/auth/photoslibrary.readonly or https://www.googleapis.com/auth/photoslibrary are included in the scopes.
Reference:
Method: mediaItems.search
If I misunderstood your question and this was not the result you want, I apologize.

Is it possible to load google photos metadata into google sheets?

I have a project where I have scanned 10,000 family pictures from as far back as the 1900's and I am organizing them in Google Photos. I have a spreadsheet where I was keeping track of the proper dates and captions for the entire collection. I would organize a few at a time but then recently found out about the google photos API.
I would like to use something like the methods Method: mediaItems.list or Method: mediaItems.search to get the data from my photos into the spreadsheet to manage.
The output from these examples is exactly what I'm looking for and would want to load that into a spreadsheet.
It would be super awesome if there was a way to update back from the sheet again as well.
I found this article but the code provided does not work for me.
I have this function now in my sheet
function photoAPI() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var albums_sh = ss.getSheetByName("albums") || ss.insertSheet("albums", ss.getSheets().length);
albums_sh.clear();
var narray = [];
var api = "https://photoslibrary.googleapis.com/v1/albums";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };
var param= "", nexttoken;
do {
if (nexttoken)
param = "?pageToken=" + nexttoken;
var response = UrlFetchApp.fetch(api + param, options);
var json = JSON.parse(response.getContentText());
json.albums.forEach(function (album) {
var data = [
album.title,
album.mediaItemsCount,
album.productUrl
];
narray.push(data);
});
nexttoken = json.nextPageToken;
} while (nexttoken);
albums_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
When I run it in debug mode, I get the following error
({error:{code:403, message:"Request had insufficient authentication scopes.", status:"PERMISSION_DENIED"}})
I know this means I need to authenticate but don't know how to make that happen.
I have an API key and a secret from the Google photos API pages.
Edit
I used the links from #Tanaike to figure out how to add scopes to my project.
I added these three.
spreadsheets.currentonly
photoslibrary
script.external_request
Now when I run in debug mode, I get a 403 error indicating I need to set up my API. Summary of the error is below:
error:
code:403
Photos Library API has not been used in project 130931490217 before or it is disabled. Enable it by visiting
https://console.developers.google.com/apis/api/photoslibrary.googleapis.com/overview?project=130931490217
Google developers console API activation
type.googleapis.com/google.rpc.Help
"PERMISSION_DENIED"
When I try to go to the listed URL though, I just get a message that says "Failed to load."
I got my code working with the help of #Tanaike in my comments above. I had two issues.
1) I needed to specify the oauthScopes in appsscript.json which is hidden by default in google scripts. It can be revealed by going to the menu and selecting View > Show Manifest File.
2) I was using a default GCP project which did not have authorization to use the photos API and could not be enabled. I needed to switch to a standard GCP project which I had created earlier and had enabled the photos API.
Here is my original posted function with additional comments after I got it working:
function photoAPI_ListAlbums() {
// Modified from code by Stackoverflow user Frç Ju at https://stackoverflow.com/questions/54063937/0auth2-problem-to-get-my-google-photos-libraries-in-a-google-sheet-of-mine
// which was originally Modified from http://ctrlq.org/code/20068-blogger-api-with-google-apps-script
/*
This function retrieves all albums from your personal google photos account and lists each one with the name of album, count of photos, and URL in a new sheet.
Requires Oauth scopes. Add the below line to appsscript.json
"oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/photoslibrary", "https://www.googleapis.com/auth/photoslibrary.readonly", "https://www.googleapis.com/auth/script.external_request"]
Also requires a standard GCP project with the appropriate Photo APIs enabled.
https://developers.google.com/apps-script/guides/cloud-platform-projects
*/
//Get the spreadsheet object
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Check for presence of target sheet, if it does not exist, create one.
var albums_sh = ss.getSheetByName("albums") || ss.insertSheet("albums", ss.getSheets().length);
//Make sure the target sheet is empty
albums_sh.clear();
var narray = [];
//Build the request string. Default page size is 20, max 50. set to max for speed.
var api = "https://photoslibrary.googleapis.com/v1/albums?pageSize=50";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };
var param= "", nexttoken;
//Make the first row a title row
var data = [
"Title",
"Item Count",
"ID",
"URL"
];
narray.push(data);
//Loop through JSON results until a nextPageToken is not returned indicating end of data
do {
//If there is a nextpagetoken, add it to the end of the request string
if (nexttoken)
param = "&pageToken=" + nexttoken;
//Get data and load it into a JSON object
var response = UrlFetchApp.fetch(api + param, options);
var json = JSON.parse(response.getContentText());
//Loop through the JSON object adding desired data to the spreadsheet.
json.albums.forEach(function (album) {
var data = [
"'"+album.title, //The prepended apostrophe makes albums with a name such as "June 2007" to show up as that text rather than parse as a date in the sheet.
album.mediaItemsCount,
album.id,
album.productUrl
];
narray.push(data);
});
//Get the nextPageToken
nexttoken = json.nextPageToken;
//Continue if the nextPageToaken is not null
} while (nexttoken);
//Save all the data to the spreadsheet.
albums_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
And here is another function which I created in the same style to pull photo metadata directly. This is what I was originally trying to accomplish.
function photoAPI_ListPhotos() {
//Modified from above function photoAPI_ListAlbums
/*
This function retrieves all photos from your personal google photos account and lists each one with the Filename, Caption, Create time (formatted for Sheet), Width, Height, and URL in a new sheet.
it will not include archived photos which can be confusing if you happen to have a large chunk of archived photos some pages may return only a next page token with no media items.
Requires Oauth scopes. Add the below line to appsscript.json
"oauthScopes": ["https://www.googleapis.com/auth/spreadsheets.currentonly", "https://www.googleapis.com/auth/photoslibrary", "https://www.googleapis.com/auth/photoslibrary.readonly", "https://www.googleapis.com/auth/script.external_request"]
Also requires a standard GCP project with the appropriate Photo APIs enabled.
https://developers.google.com/apps-script/guides/cloud-platform-projects
*/
//Get the spreadsheet object
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Check for presence of target sheet, if it does not exist, create one.
var photos_sh = ss.getSheetByName("photos") || ss.insertSheet("photos", ss.getSheets().length);
//Make sure the target sheet is empty
photos_sh.clear();
var narray = [];
//Build the request string. Max page size is 100. set to max for speed.
var api = "https://photoslibrary.googleapis.com/v1/mediaItems?pageSize=100";
var headers = { "Authorization": "Bearer " + ScriptApp.getOAuthToken() };
var options = { "headers": headers, "method" : "GET", "muteHttpExceptions": true };
//This variable is used if you want to resume the scrape at some page other than the start. This is needed if you have more than 40,000 photos.
//Uncomment the line below and add the next page token for where you want to start in the quotes.
//var nexttoken="";
var param= "", nexttoken;
//Start counting how many pages have been processed.
var pagecount=0;
//Make the first row a title row
var data = [
"Filename",
"description",
"Create Time",
"Width",
"Height",
"ID",
"URL",
"NextPage"
];
narray.push(data);
//Loop through JSON results until a nextPageToken is not returned indicating end of data
do {
//If there is a nextpagetoken, add it to the end of the request string
if (nexttoken)
param = "&pageToken=" + nexttoken;
//Get data and load it into a JSON object
var response = UrlFetchApp.fetch(api + param, options);
var json = JSON.parse(response.getContentText());
//Check if there are mediaItems to process.
if (typeof json.mediaItems === 'undefined') {
//If there are no mediaItems, Add a blank line in the sheet with the returned nextpagetoken
//var data = ["","","","","","","",json.nextPageToken];
//narray.push(data);
} else {
//Loop through the JSON object adding desired data to the spreadsheet.
json.mediaItems.forEach(function (MediaItem) {
//Check if the mediaitem has a description (caption) and make that cell blank if it is not present.
if(typeof MediaItem.description === 'undefined') {
var description = "";
} else {
var description = MediaItem.description;
}
//Format the create date as appropriate for spreadsheets.
var d = new Date(MediaItem.mediaMetadata.creationTime);
var data = [
MediaItem.filename,
"'"+description, //The prepended apostrophe makes captions that are dates or numbers save in the sheet as a string.
d,
MediaItem.mediaMetadata.width,
MediaItem.mediaMetadata.height,
MediaItem.id,
MediaItem.productUrl,
json.nextPageToken
];
narray.push(data);
});
}
//Get the nextPageToken
nexttoken = json.nextPageToken;
pagecount++;
//Continue if the nextPageToaken is not null
//Also stop if you reach 400 pages processed, this prevents the script from timing out. You will need to resume manually using the nexttoken variable above.
} while (pagecount<400 && nexttoken);
//Continue if the nextPageToaken is not null (This is commented out as an alternative and can be used if you have a small enough collection it will not time out.)
//} while (nexttoken);
//Save all the data to the spreadsheet.
photos_sh.getRange(1, 1, narray.length, narray[0].length).setValues(narray);
}
Because of the limitations of the ListPhotos function and the fact that my library is so enormous, I am still working on a third function to pull photo metadata from all the photos in specific albums. I'll edit this answer once I pull that off.

Is there a setTitle() similar to getTitle() of a page element?

As the title suggests, I'm looking for a way to set the alt title of an image in a slideshow.
Currently this is what i have tried, but for some reason it doesn't seem to update:
var resource = {"requests": [
{"updatePageElementAltText": {
"objectId": id,
"description": "",
"title": elementTitle
}
}]};
Slides.Presentations.batchUpdate(resource, presentationId);
It might be worth noting that the script is running in the Script Editor of a google sheet. The variables id, elementTitle and presentationId are all defined earlier in the script and I've checked that they are correct.
Can anyone spot the issue with this or suggest an easier way to do it?
Edit: Tanaike helped me make this specific part of the script work, but it isn't working in the larger picture, hence this edit.
What the script is supposed to do, is basically do a find/replace on all Image elements in the slideshow.
Based on keys in a sheet in Column A it should replace the Image URL in with the corresponding URL in column B. The script then cycles through all elements in the slideshow, finds the images, and then cycles through them to check if any of the titles have the 'key' as the title. The image URL should then be replaced with the URL on the same row in sheet. This part of the script is tested and works, but the key is removed from the object when the URL is updated. This shouldn't be happening as the Image should be able to be replaced again later.
For this reason, I tried to save the title before updating the URL and the put it back with the above-mentioned batchUpdate, but for some reason, it isn't working properly.
Here is the full script:
function imageReplacer() {
var newPresentationSlides = SlidesApp.openByUrl(myslidesurl).getSlides();
var imageTitles = SpreadsheetApp.openByUrl(mysheeturl).getRange("'Image Replace List'!A2:A").getValues();
var imageURLs = SpreadsheetApp.openByUrl(mysheeturl).getRange("'Image Replace List'!B2:B").getValues();
var presentationId = 'myslidesid';
for (y = 0; y < newPresentationSlides.length; y++) {
var pageElements = newPresentationSlides[y].getPageElements();
for (x = 0; x < pageElements.length; x++) {
for (a = 0; a < imageTitles.filter(String).length; a++) {
if (pageElements[x].getPageElementType() == "IMAGE") {
if(pageElements[x].asImage().getTitle() == imageTitles[a]) {
var elementTitle = pageElements[x].asImage().getTitle();
var id = pageElements[x].getObjectId();
pageElements[x].asImage().replace(imageURLs[a]);
var id = pageElements[x].getObjectId();
var resource = {"requests": [
{"updatePageElementAltText": {
"objectId": id,
"description": "Sample description",
"title": elementTitle
}
}]};
Slides.Presentations.batchUpdate(resource, presentationId);
}
}
}
}
}
}
As you can see the middle part of the script is exactly the same as tanaike suggested, but it's just not working properly (I even tested that specific part as a stand-alone script and it worked fine.).
Second edit:
Examples:
Sheet: https://docs.google.com/spreadsheets/d/1npWyONio_seI3bRibFWxiqzHxLZ-ie2wbszgROkLduE/edit#gid=0
Slides: https://docs.google.com/presentation/d/1rfT7TLD-O7dBbwV5V3UbugN1OLOnBI2-CZN2GPnmANM/edit#slide=id.p
I think that your script works. You can confirm the updated result on the slide.
But if you want to retrieve the title and description using Slides services like getTitle() and getDescription() after the title and description are updated using Slides API, it seems that those results are not updated. The updated results couldn't be retrieved even if saveAndClose() is used. And also, unfortunately, in the current stage, I couldn't find the methods like setTitle() and setDescription() in my environment. So how about this workaround? In this workaround, the title and description are updated by Slides API and those are retrieved by Slides API.
Sample script:
var presentationId = "###"; // Please set this.
var objectId = "###"; // Please set this.
// Update title and description
var resource = {"requests": [
{"updatePageElementAltText": {
"objectId": objectId,
"description": "Sample description",
"title": "Sample title"
}
}]};
Slides.Presentations.batchUpdate(resource, presentationId);
// Retrieve updated title and description
var res = Slides.Presentations.get(presentationId);
var slides = res.slides;
for (var i = 0; i < slides.length; i++) {
var pe = slides[i].pageElements;
for (var j = 0; j < pe.length; j++) {
if (pe[j].objectId == objectId) {
Logger.log(pe[j].title)
Logger.log(pe[j].description)
break;
}
}
}
Note:
If you use this script, please enable Slides API at Advanced Google Services and API console.
References:
presentations.batchUpdate
presentations.get
If I misunderstand what you want, I'm sorry.
Edit:
You want to replace all images in Slides.
At this time, you want to search the title of each image and replace the image from URL using the title.
When the images are replaced, you don't want to change the title (key) of each image.
If my understanding is correct, how about this modification?
Modification points:
It seems that when the image is replaced, the title of image is cleared.
In order to avoid this, when the image is replaced, it also puts the title. For this situation, batchUpdate of Slides API is used.
From the viewpoint of the process cost, at first, it creates the request body and requests the request body. By this, this situation can be achieved by only one API call.
Modified script:
function imageReplacer() {
var spreadsheetId = "### spreadsheetId ###"; // Please modify this.
var sheetName = "Image Replace List";
var presentationId = "### presentationId ###"; // Please modify this.
var sheet = SpreadsheetApp.openById(spreadsheetId).getSheetByName(sheetName);
var values = sheet.getRange(2, 1, sheet.getLastRow(), 2).getValues().filter(function(e) {return e[0] && e[1]});
var s = SlidesApp.openById(presentationId);
var slides = s.getSlides();
var requests = slides.reduce(function(reqs, slide) {
var r = slide.getPageElements().reduce(function(ar, e) {
if (e.getPageElementType() == "IMAGE") {
var key = values.filter(function(v) {return v[0] == e.getTitle()});
if (key.length == 1) {
var id = e.getObjectId();
var rq = [
{"replaceImage":{"imageObjectId":id, "url": key[0][1]}},
{"updatePageElementAltText":{"objectId":id, "title": key[0][0]}}
];
Array.prototype.push.apply(ar, rq);
}
}
return ar;
}, []);
if (r.length > 0) Array.prototype.push.apply(reqs, r);
return reqs;
}, []);
Slides.Presentations.batchUpdate({requests: requests}, presentationId);
}
Note:
I'm not sure about the maximum number of requests for one API call. So if you want to replace a lot of images, if the error due to this occurs, please modify above script.

Google Apps Script (GAS) Using urlfetchapp with username and password

I am not a coder by nature and am self taught in GAS (only code I have used). I work for City College Norwich and I would like to create a script that automatically logs me in to their website so I can fetch timetable data and put it into a spreadsheet.
After doing some research I have given up trying to figure it out so I am asking for help.
I have tried this:
function getTimetables() {
var url = "https://ccn.ac.uk/user/";
var options = {
"method": "post",
"payload": {
"user-login" : "username",
"edit-pass" : "password",
"BUTTON_Submit" : "Log In",
},
"testcookie": 1,
"followRedirects": false
};
var response = UrlFetchApp.fetch(url, options);
if ( response.getResponseCode() == 200 ) {
// Incorrect user/pass combo
Logger.log('Incorrect user/pass combo')
} else if ( response.getResponseCode() == 302 ) {
// Logged-in
var headers = response.getAllHeaders();
if ( typeof headers['Set-Cookie'] !== 'undefined' ) {
// Make sure that we are working with an array of cookies
var cookies = typeof headers['Set-Cookie'] == 'string' ? [ headers['Set-Cookie'] ] : headers['Set-Cookie'];
for (var i = 0; i < cookies.length; i++) {
// We only need the cookie's value - it might have path, expiry time, etc here
cookies[i] = cookies[i].split( ';' )[0];
};
url = "https://mytimetable.ccn.ac.uk/timetable.aspx?week=30&room=C5A";
options = {
"method": "get",
// Set the cookies so that we appear logged-in
"headers": {
"Cookie": cookies.join(';')
}
}
}
}
}
Which return "Incorrect user/pass combo".
And I have tried this:
function getTimetablev2() {
var site = "https://ccn.ac.uk/user"
var USERNAME = PropertiesService.getScriptProperties().getProperty('username');
var PASSWORD = PropertiesService.getScriptProperties().getProperty('password');
var url = PropertiesService.getScriptProperties().getProperty(site);
var headers = {
"Authorization" : "Basic " + Utilities.base64Encode(USERNAME + ':' + PASSWORD)
};
var params
= {
"method":"GET",
"headers":headers
};
var response =
UrlFetchApp.fetch(site, params);
Logger.log(response.getResponseCode())
}
Which return code 200 - failed to log in.
If anyone can solve this for me I would be forever in your debt as it would save me loads of time. I have created a practical booking system where each teacher has their own spreadsheet with their timetable on and all bookings go to a master spreadsheet us technicians use. If I could automate generating their timetables it would be fantastic.
Without knowing how a valid HTTP POST to https://ccn.ac.uk/user/ looks, it's hard to answer your question. So...
Using hurl.it and your payload provided in the first code snippet... it doesn't look like this is a valid POST.
Using firebug and inputing dummy data into the form you are able to look at how the POST is done correctly.
These are the parameters you should use in your request body.
Your second code snippet is not very likely to work if this site doesn't natively support authentication other than using this form.