I'm trying to delete files and folders older than 7 days in a specific Team Drive (not in the whole Google Drive).
To accomplish this I tried to merge what I read here:
Apps Script - Automatically Delete Files from Google Drive Older than 3 Days - Get List of Files
I don't have enough rep to add a comment there so that's why I'm opening a new thread. Here you can find my ripoff from user1588938:
function getOldFileIDs() {
var fileIDs = [];
// Old date is 30 days
var oldDate = new Date().getTime() - 3600*1000*24*30;
var cutOffDate = Utilities.formatDate(new Date(oldDate), "GMT", "yyyy-MM-dd");
// Get folderID using the URL on google drive
var folder = DriveApp.getFolderById('XXXXXXX');
var files = folder.searchFiles('modifiedDate < "' + cutOffDate + '"');
while (files.hasNext()) {
var file = files.next();
fileIDs.push(file.getId());
Logger.log('ID: ' + file.getId() + ', Name: ' + file.getName());
}
return fileIDs;
};
function deleteFiles() {
var fileIDs = getOldFileIDs();
fileIDs.forEach(function(fileID) {
DriveApp.getFileById(fileID).setTrashed(true);
});
};
I'm stuck with 'getFolderById' function because I suppose it doesn't apply to a Team Drive root but only works for folders inside of it.
Indeed, when I look at the logs I can see that the output for:
var folder = DriveApp.getFolderById('this-is-my-team-drive-id');
is a generic: [18-07-30 06:34:49:146 PDT] Team Drive and not the name of the Team Drive I chose.
I can't go any further with the script because of this.
Any hint on how to list every file and subfolder in a Team Drive using searchFiles?
This solution might apply but it works for a folder inside a Team Drive and not on the root of the Team Drive:
browse files in google team drive
Thanks!
If someone out there is trying to achieve the same result, this is how you do it.
function deleteOldFiles() {
var Folders = new Array(
'YOUR-TEAM-DRIVE-ID' //you can find this in the team drive url
);
var DaysRetentionNumber = 15; //how many days old your files and folders must be before getting deleted?
var RetentionPeriod = DaysRetentionNumber * 24 * 60 * 60 * 1000;
Logger.clear();
for each (var FolderID in Folders) {
folder = DriveApp.getFolderById(FolderID);
processFolder(folder);
}
function processFolder(folder){
Logger.log('Folder: ' + folder.getName());
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
Logger.log('File: ' + file.getName());
if (new Date() - file.getLastUpdated() > RetentionPeriod) {
//file.setTrashed(true); //uncomment this line to put them in the trash
//Drive.Files.remove(file.getId()); //uncomment this line to delete them immediately; CAREFUL!
Logger.log('File '+ file.getName() + ' trashed');
}
}
var subfolders = folder.getFolders();
while (subfolders.hasNext()) {
subfolder = subfolders.next();
processFolder(subfolder);
}
checkEmptyFolder(folder);
}
function checkEmptyFolder(folder){
if(!folder.getFiles().hasNext() && !folder.getFolders().hasNext()){
Logger.log('Empty folder: '+ folder.getName());
folder.setTrashed(true); // put them in the trash
}
}
if(Logger.getLog() != '')
MailApp.sendEmail('youremailaddresshere', 'Team Drive weekly cleanup report', Logger.getLog()); //get a log in your email so that you can see what will be deleted; try this before uncommenting the trash/delete lines!
}
#Daniele INeDiA's script is broken due to various reasons:
The script fails withReferenceError: "Drive" is not defined. (line 24, file "Code"), which refers to //uncomment this line to delete them immediately;
The script also fails because of Access denied: DriveApp. (line 39, file "Code"), in case the root folder is empty. It's because it doesn't differentiate between the root folder and its sub folders.
I've fixed #1 by simply leaving it commented out, but here's a fixed version for #2.
While at it, I've also added a feature to always delete (always_delete on/off). That way you can schedule it to run every night to make sure you start the day with an empty folder.
Also while at it, I've added an on/off switch to send a log by e-mail.
P.S.
I did see tehhowch's comment that "for each (... in ...) is deprecated", so feel free to add an alternative and I'll use it instead.
function deleteOldFiles() {
var sendlog = false, Folders = new Array(
'YOUR-TEAM-DRIVE-ID' //you can find this in the team drive url
);
var DaysRetentionNumber = 15; //how many days old your files and folders must be before getting deleted?
var RetentionPeriod = DaysRetentionNumber * 24 * 60 * 60 * 1000;
var always_delete = true;
Logger.clear();
for each (var FolderID in Folders) {
folder = DriveApp.getFolderById(FolderID);
processFolder(folder, FolderID);
}
function processFolder(folder, FolderID){
Logger.log('Folder: ' + folder.getName());
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
if (!always_delete)
Logger.log('File: ' + file.getName());
if (always_delete || new Date() - file.getLastUpdated() > RetentionPeriod) {
file.setTrashed(true); //uncomment this line to put them in the trash
// Don't uncomment the following because it breaks the script!
//Drive.Files.remove(file.getId()); //uncomment this line to delete them immediately; CAREFUL!
Logger.log('File '+ file.getName() + ' trashed');
}
}
var subfolders = folder.getFolders();
while (subfolders.hasNext()) {
subfolder = subfolders.next();
processFolder(subfolder);
}
if(Folders.indexOf(FolderID) == -1)
checkEmptyFolder(folder);
}
function checkEmptyFolder(folder){
if(!folder.getFiles().hasNext() && !folder.getFolders().hasNext()){
Logger.log('Empty folder: '+ folder.getName());
folder.setTrashed(true); // put them in the trash
}
}
if(sendlog && Logger.getLog() != '')
MailApp.sendEmail('youremailaddresshere', 'Team Drive cleanup report', Logger.getLog()); //get a log in your email so that you can see what will be deleted; try this before uncommenting the trash/delete lines!
}
In my case I needed variability of how long to keep files based on what top folder they are in. Some folders have thousands of generated imgs that need to be cleaned up often and other folders have reports that should be kept around for longer. There are a lot of nested folders as well. This script has to be triggered a lot to keep up. Any tips on optimization would be great!
I have not been able to find an example quite like this, so I hope it can be useful to someone searching as I had searched.
/**
* Clean up Google Drive folders sending all files
* last modified older than daysToKeep to the trash
*/
function cleanUpFolders() {
let folders = [
{ id: "folderId1", daysToKeep: 14 },
{ id: "folderId2", daysToKeep: 30 },
{ id: "folderId3", daysToKeep: 90 }
];
for (let f = 0; f < folders.length; f++) {
let folder = DriveApp.getFolderByIdAndResourceKey(folders[f].id, "");
let daysAgo = getDaysAgo(folders[f].daysToKeep);
cleanUpFolder(folder, daysAgo);
}
}
/**
* Cleans up a folder sending all files last modified more than filterDate to the trash
*
* #param {DriveApp.Folder} folder to proccess
* #param {String} filterDate date to search files older than in format YYYY-MM-DD
*/
function cleanUpFolder(folder, filterDate) {
console.log("searching in " + folder.getName());
let trashCount = 0;
// Send files last modified before filterDate to the trash
let query = 'modifiedDate < "' + filterDate + '"';
let files = folder.searchFiles(query);
while (files.hasNext()) {
let file = files.next();
file.setTrashed(true);
// console.log(file.getName() + " modified " + formatDate(file.getLastUpdated()));
trashCount++;
}
// Recursively clean up all sub folders
let folders = folder.getFolders();
while (folders.hasNext()) {
trashCount += cleanUpFolder(folders.next(), filterDate);
}
// Check if the folder is now empty and trash it as well
if (isEmptyFolder(folder)) {
folder.setTrashed(true);
console.log(folder.getName() + " is empty and has been trashed");
}
console.log(trashCount + " files found last modified before " + filterDate + " in " + folder.getName() + " have been trashed");
return trashCount;
}
/**
* Check if a folder has any files or folders
*
* #param {DriveApp.Folder} folder to check
*/
function isEmptyFolder(folder) {
return !folder.getFiles().hasNext() && !folder.getFolders().hasNext();
}
/**
* get date string "YYYY-MM-DD" days ago from now
*
* #param {number} days ago from now
*/
function getDaysAgo(days) {
let date = new Date(Date.now());
let nowString = formatDate(date);
date.setDate(date.getDate() - days);
let daysAgoString = formatDate(date);
console.log("now: " + nowString + ", " + days + " days ago: " + daysAgoString);
return daysAgoString;
}
/**
* Format date as "YYYY-MM-DD"
*
* #param {Date} date to format
*/
function formatDate(date) {
return date.getFullYear() + '-' +
(date.getMonth()+1).toString().padStart(2,'0') + '-' +
date.getDate().toString().padStart(2, '0');
}
Related
I have the following script in a Google Sheet:
/**
* Create CSV file of Sheet2
* Modified script written by Tanaike
* https://stackoverflow.com/users/7108653/tanaike
*
* Additional Script by AdamD.PE
* version 13.11.2022.1
* https://support.google.com/docs/thread/188230855
*/
/** Date extraction added by Tyrone */
const date = new Date();
/** Extract today's date */
let day = date.getDate();
let month = date.getMonth() + 1;
let year = date.getFullYear();
if (day < 10) {
day = '0' + day;
}
if (month < 10) {
month = `0${month}`;
}
/** Show today's date */
let currentDate = `${day}-${month}-${year}`;
/** Date extraction added by Tyrone */
function sheetToCsvModelo0101() {
var filename = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getSheetName() + "-01" + " - " + currentDate; // CSV file name
filename = filename + '.csv';
var ssid = SpreadsheetApp.getActiveSpreadsheet().getId();
var folders = DriveApp.getFileById(ssid).getParents();
var folder;
if (folders.hasNext()) {
folder = folders.next();
var user = Session.getEffectiveUser().getEmail();
if (!(folder.getOwner().getEmail() == user || folder.getEditors().some(e => e.getEmail() == user))) {
throw new Error("This user has no write permission for the folder.");
}
} else {
throw new Error("This user has no write permission for the folder.");
}
var SelectedRange = "A2:AB3";
var csv = "";
var v = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(SelectedRange).getValues();
v.forEach(function (e) {
csv += e.join(",") + "\n";
});
var newDoc = folder.createFile(filename, csv, MimeType.CSV);
console.log(newDoc.getId()); // You can see the file ID.
}
This script basically creates a .CSV file in the same folder where the worksheet is, using the range defined in var SelectedRange.
This script is applied to a button on the worksheet.
The question is: how do I make every comma typed in this spreadsheet be converted into another sign, like # before generating the .CSV file in the folder?
I would also like to know if instead of generating 1 file in the folder it is possible to generate 2 files, each with a name.
Issue:
The question is: how do I make every comma typed in this spreadsheet be converted into another sign, like # before generating the .CSV file in the folder?
After you get the sheet values via getValues, replace all instances of , in the resulting 2D array with #, using map and replaceAll.
I think this is a better approach than TextFinder since sheet values are not modified.
Code snippet:
From your original sample, just add the following line:
// ...stuff...
var v = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(SelectedRange).getValues();
v = v.map(r => r.map(c => c.replaceAll(",", "#"))); // Add this line
v.forEach(function (e) {
csv += e.join(",") + "\n";
});
// ...stuff...
If you are doing this to avoid conflicts between the comma in the cells and the csv delimiter then try doing the csv like this:
function sheetToCsv() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0")
const params = { "method": "GET", "headers": { "Authorization": "Bearer " + ScriptApp.getOAuthToken() } };
const url = "https://docs.google.com/spreadsheets/d/" + ss.getId() + "/export?gid=" + sh.getSheetId() + "&format=csv";
const r = UrlFetchApp.fetch(url, params);
const csv = r.getContentText();
return csv;
}
And then put it back in a spreadsheet like this:
function csvToSheet(csv) {
const vs = Utilities.parseCsv(csv,',');
const osh = ss.getSheetByName("Sheet1");
osh.getRange(1,1,vs.length,vs[0].length).setValues(vs);
}
In the meantime I've found a solution that almost works the way I'd like.
I created 2 functions, one to convert , to # and another to convert # to , again, then after the .csv file creation is complete the script switches back from # to , .
/**
* Create CSV file of Sheet2
* Modified script written by Tanaike
* https://stackoverflow.com/users/7108653/tanaike
*
* Additional Script by AdamD.PE
* version 13.11.2022.1
* https://support.google.com/docs/thread/188230855
*/
var SelectedRange = "A2:AB3";
function searchAndReplace_ToHash() {
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(SelectedRange).createTextFinder(',').replaceAllWith('#');
}
function searchAndReplace_ToComma() {
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(SelectedRange).createTextFinder('#').replaceAllWith(',');
}
function sheetToCsv_02() {
var filename = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getSheetName() + "-01" + " - " + currentDate; // CSV file name
filename = filename + '.csv';
var ssid = SpreadsheetApp.getActiveSpreadsheet().getId();
searchAndReplace_ToHash()
// I modified below script.
var folders = DriveApp.getFileById(ssid).getParents();
var folder;
if (folders.hasNext()) {
folder = folders.next();
var user = Session.getEffectiveUser().getEmail();
if (!(folder.getOwner().getEmail() == user || folder.getEditors().some(e => e.getEmail() == user))) {
throw new Error("This user has no write permission for the folder.");
}
} else {
throw new Error("This user has no write permission for the folder.");
}
var csv = "";
var v = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(SelectedRange).getValues();
v.forEach(function (e) {
csv += e.join(",") + "\n";
});
var newDoc = folder.createFile(filename, csv, MimeType.CSV);
console.log(newDoc.getId()); // You can see the file ID.
searchAndReplace_ToComma()
}
It solves the problem, but it would be perfect if this change was not visible in the spreadsheet.
Is it possible to make this substitution without displaying it in the spreadsheet?
As for your script suggestion, I would like to change as little as possible in this script I'm using, it works exactly the way I need it to work, except for the fact that the commas of words conflict with the column divisions.
Anyway, thank you very much for all your attention and patience!
I am trying to create a script that will work through a shared drive once a week, and delete anything that has not been modified within the last 90 days.
I have found a few scripts on here that do not seem to work as I intended them to.
The script I currently have is below, but for some reason it does not seem to work, yet it seems as though it should.
It seems to identify the files, but does not seem to move them to the trash.
Any tips would be welcome thank you.
function getOldFileIDs() {
var fileIDs = [];
// Old date is 90 days
var oldDate = new Date().getTime() - 3600*1000*24*90;
var cutOffDate = Utilities.formatDate(new Date(oldDate), "GMT", "yyyy-MM-dd");
// Get folderID using the URL on google drive
var folder = DriveApp.getFolderById('1ClpJ8uwlVRc9zT4q2AsatEQBvPFTH5Eu');
var files = folder.searchFiles('modifiedDate < "' + cutOffDate + '"');
while (files.hasNext()) {
var file = files.next();
fileIDs.push(file.getId());
Logger.log('ID: ' + file.getId() + ', Name: ' + file.getName());
}
return fileIDs;
};
function deleteFiles() {
var fileIDs = getOldFileIDs();
fileIDs.forEach(function(fileID) {
DriveApp.getFileById(fileID).setTrashed(true);
});
};
Unfortunately I was having a bad day, what I forgot to do is change the folder id from the test ID to the working ID
I'd like to create some folders in Google Drive through a script in Google Sheets and then to get the URLs of the new folders to put them in a cell.
I successfully created the folders based on the column "Employee ID" and replaced the values of the column "Employee ID" with the folder hyperlinks.
So I get the 3 folders created in Google Drive: 1,2 and 3, respectively. The problem is that my code repeats the same name and URL in every row in the Google Sheets setting the name and URL of the last folder that was created (the folder 3 in this case).
I would appreciate it if you could give me some help with this. This is my code:
function onEdit(e) {
if ([1, 2,].indexOf(e.range.columnStart) != 1) return;
createEmployeeFolder();
}
function createEmployeeFolder() {
var parent = DriveApp.getFolderById("1H0i69rE9WO0IAoxhnrFY2YKT_tD50fuX")
SpreadsheetApp.getActive().getSheetByName('Database').getRange('B3:B').getValues()
.forEach(function (r) {
if(r[0]) checkIfFolderExistElseCreate(parent, r[0]);
})
}
function checkIfFolderExistElseCreate(parent, folderName) {
var folder;
var idfolder;
var link;
try {
folder = parent.getFoldersByName(folderName).next();
} catch (e) {
folder = parent.createFolder(folderName);
idfolder = folder.getId();
link = folder.getUrl();
formula = '=hyperlink("' + link + '",' + folder + ')';
SpreadsheetApp.getActive().getSheetByName('Database').getRange('B3:B').setFormula(formula);
}
}
Ok I changed my code for this. I can create the folders but the problem is with the SetFormula I can't make it work in order to read every row to replace the ID with the URL. It just take the last ID and then it repeats the same ID in all the rows from the range. Please some help! :P
function createEmployeeFolder() {
var parent = DriveApp.getFolderById("1H0i69rE9WO0IAoxhnrFY2YKT_tD50fuX")
SpreadsheetApp.getActive().getSheetByName('Database').getRange('B3:B').getValues()
.forEach(function (r) {
if(r[0]) createFolder(r[0]);
})
}
function createFolder(folderName) {
var parent = DriveApp.getFolderById("1H0i69rE9WO0IAoxhnrFY2YKT_tD50fuX");
var projectFolder;
if (parent.getFoldersByName(folderName).hasNext()) {
// folder already exists
Folder = parent.getFoldersByName(folderName).next();
} else {
Folder = parent.createFolder(folderName);
}
var id = Folder.getId();
var link = Folder.getUrl();
var formula = '=hyperlink("' + link + '",' + Folder + ')';
SpreadsheetApp.getActive().getSheetByName('Database').getRange('B3:B').setFormula(formula);
return formula;
}
I have a function that does this for me. It takes a userName and returns a formula for a link to the folder that either existed or was created. This link can be put into a cell using setFormula():
function createUserFolder(userName) {
var parent = DriveApp.getFolderById("1H0i69rE9WO0IAoxhnrFY2YKT_tD50fuX");
if (parent.getFoldersByName(userName).hasNext()) {
// folder already exists
userFolder = parent.getFoldersByName(userName).next();
} else {
userFolder = parent.createFolder(userName);
}
var id = userFolder.getId();
var formula = '=HYPERLINK("https://drive.google.com/open?id=' + id + '","Files")'
return formula;
}
Please note however that you should rethink your code quite dramatically. onEdit is called for every single change in the spreadsheet which is very wasteful. Iterating through every single cell in column B is very wasteful. Using the catch clause in a try to do your critical code is a bad idea.
I would add a dialog or other ui to the sheet for triggering the creation of the folder rather than monitoring the sheet continually.
Solution:
The following script creates a folder in drive and a link with the name of the folder within a cell automatically.
function createAndHyperlink() {
var ss, sh, parent, parent2, r, v, thisCell, folder
ss = SpreadsheetApp.getActive()
sh = ss.getSheetByName('INSERTTHENAMEOFYOURSHEETHERE')
parent = DriveApp.getFileById(ss.getId()).getParents().next();
parent2 = DriveApp.getFolderById("INSERT-YOURGOOGLEDRIVEFOLDERIDEHERE")
r = sh.getRange('B3:B')
v = r.getValues()
for (var i = 0, l = v.length; i < l; i++) {
thisCell = sh.getRange(i + 3, 2)
if (v[i][0] && !thisCell.getFormula()) {
folder = parent2.createFolder(v[i][0]);
thisCell.setFormula('=HYPERLINK("' + folder.getUrl() + '"; "' + v[i][0] + '")');
}
}
}
Your issue stems from incomplete refactoring - you split the task of getting the folder id into a separate method, but also split the task of setting the hyperlink into that Range-unaware code.
The solution to repeating the last value across all other rows is to fix your refactoring - setting the formulas should be done where the range to write to is known.
function createEmployeeFolder() {
const parent = DriveApp.getFolderById("some id");
const sheet = SpreadsheetApp.getActive().getSheetByName('Database');
const range = sheet.getRange(3, 2, sheet.getLastRow() - 2);
range.getValues().forEach(function (row, index) {
// Consider checking if this r & c has a formula in the equal size array from getFormulas()
if(row[0]) {
var newLink = getLinkForFolderName_(parent, row[0]);
// Use the current array index to write this formula in only the correct cell.
range.offset(index, 0, 1).setFormula(
"=hyperlink(\"" + newLink + "\", \"" + row[0] + "\")";
);
}
});
}
function getLinkForFolderName_(root, name) {
var folder;
const search = root.getFoldersByName(name);
if (search.hasNext()) {
folder = search.next();
if (search.hasNext())
console.warn("Multiple folders named '" + name + "' in root folder '" + root.getName() + "'");
}
else
folder = root.createFolder(name);
return folder.getUrl();
}
You should do away with the onEdit simple trigger binding since simple triggers cannot create folders (you need authorization for that) - just change the name and install the on edit trigger for the new name. Another option is to use a menu option that calls this function. However, one (possible) benefit of using an installed trigger is that all folders will be owned by the same account.
I want to identify duplicate files in Google drive and delete them. please guide or correct mycode.
while (folder.hasNext()) {
var folders = folder.next();
var Getfiles = folders.getFilesByName(file.getName());
Logger.log("Folder Name: " + folders.getName());
while (Getfiles.hasNext()) {
var Getfile = Getfiles.next();
if(Getfile.getFilesByName(Getfile.getName()) >1)
//I don't know what to do here
}
}
My question for you would be how do you define a file as duplicate you appear to be just checking if the name is the same. What about files in different directories, files of different sizes, or if they have different dates. Which one do you want to keep?
If I was doing this I would check file name, location and date to ensure that I have the newest file.
I would run a file.list and get a list of every file on my Google drive account. Then i would sort the files locally by directory then by name and date. Once the files are sorted i would run though them if there was more then one file i would send a file.delete to Google for each of the duplicate files. The issue here is that you are going to have to do all the comparing locally.
Technically speaking you could also do a file.list then loop though each file and make another file.list request to google asking with the q parameter looking for other files of the same name. Check what is returned to and delete any of the returned files that are in the same directory as the file we are checking and have a younger date. This solution is going to eat more of your quota as you are making more requests to Google.
This is my working code, I hope it can help some of you guys who may need something similar.
for (var msgIdx=0; msgIdx<messages.length; msgIdx++) {
var message = messages[msgIdx];
Logger.log("INFO: Processing message: "+message.getSubject() + " (" + message.getId() + ")");
var messageDate = message.getDate();
var attachments = message.getAttachments();
for (var attIdx=0; attIdx<attachments.length; attIdx++) {
var attachment = attachments[attIdx];
Logger.log("INFO: Processing attachment: "+attachment.getName());
try {
var folder = getOrCreateFolder(rule.folder);
var getFoldersByName = DriveApp.getFoldersByName(rule.folder);
var newFilename = Utilities.formatDate(messageDate, config.timezone, rule.filenameTo.replace('%s',message.getSubject()));
while (getFoldersByName.hasNext()) {
var FetchhedFolders = getFoldersByName.next();
var GetFiles = FetchhedFolders.getFiles();
while (GetFiles.hasNext()) {
var files = GetFiles.next();
var GetFileName = FetchhedFolders.getFilesByName(newFilename);
if(GetFileName.hasNext() )
{
FetchhedFolders.removeFile(newFilename);
}else if (!GetFileName.hasNext())
{
var file = folder.createFile(attachment);
if (rule.filenameFrom && rule.filenameTo && rule.filenameFrom == file.getName()) {
Logger.log("INFO: Renaming matched file '" + file.getName() + "' -> '" + newFilename + "'");
file.setName(newFilename);
}
else if (rule.filenameTo) {
var newFilename = Utilities.formatDate(messageDate, config.timezone, rule.filenameTo.replace('%s',message.getSubject()));
Logger.log("INFO: Renaming '" + file.getName() + "' -> '" + newFilename + "'");
file.setName(newFilename);
}
file.setDescription("Mail title: " + message.getSubject() + "\nMail date: " + message.getDate() + "\nMail link: https://mail.google.com/mail/u/0/#inbox/" + message.getId());
Utilities.sleep(config.sleepTime);
}
}
}
} catch (e) {
Logger.log(e);
}
}
}
I have set up google sheets to monitor an a folder for updates. When a file is uploaded within the last 24 hours, it updates my google sheet (perfect). Then it messages the designated slack channel (perfect). I want this script to run every minute and only send an update to slack when a new file was uploaded in that 1 minute time frame. How do I update the code?!?
Problems
1) if no files updated, it sends a message to slack still saying undefined for the file URL
problem message in slack
2) if multiple files uploaded, and the google sheet updates, slack only gets a message for one of the files. only one link sent to slack image
Thoughts
1) I could add a function for slack and create a hourly trigger to check the drive file for updates then a trigger for the slack function to go off
2) regardless if one solves it, I need to figure out the code so there is no slack message when there is no update.
/***************************************************
Script will send an slack notification to Slack Channel
when a file is uploaded to monitored Drive Upload folder. SDH 5.29.17
***************************************************/
function checkForChangedFiles() {
// edit this line below with the ID "XXXXXXXxxXXXwwerr0RSQ2ZlZms" of the folder you want to monitor for changes
var folderID = '"' + "XXXXX" + '"';
var folderSearch = folderID + " " + "in parents";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// Get the spreadsheet slack channel
var channel = sheet.getRange("F1").getValue();
// Get the spreadsheet time zone
var timezone = ss.getSpreadsheetTimeZone();
var today = new Date();
// Find files modified in the last 24 hours, Run script next day, and set below to 24 hours
// 60 * 1000 = 60 second
// 60* (60 * 1000) = 60 mins which is 1 hour
// 24* (60* (60 * 1000)) = 1 day which 24 hours
var oneDayAgo = new Date(today.getTime() - 1 * 60 * 1000);
// var oneDayAgo = new Date(today.getTime() - 1 * 60 * 1000);
var startTime = oneDayAgo.toISOString();
var search = '(trashed = true or trashed = false) and '+ folderSearch +' and (modifiedDate > "' + startTime + '")';
var files = DriveApp.searchFiles(search);
var row = "", count=0;
while( files.hasNext() ) {
var file = files.next();
var fileName = file.getName();
var fileURL = file.getUrl();
var lastUpdated = Utilities.formatDate(file.getLastUpdated(), timezone, "yyyy-MM-dd HH:mm");
var dateCreated = Utilities.formatDate(file.getDateCreated(), timezone, "yyyy-MM-dd HH:mm")
row += "<li>" + lastUpdated + " <a href='" + fileURL + "'>" + fileName + "</a></li>";
sheet.appendRow([dateCreated, lastUpdated, fileName, fileURL]);
count++;
}
// add function for SLACK? then I can have slack run for each update and the first function run daily :)
var url = "https://hooks.slack.com/services/XXXX";
var payload = {
"channel" : channel,
"username" : "DriveUpload", // <-- optional parameter, use if you want to override default "robot" name
"text" : "Upload! *''"+fileName+"''* "+ fileURL, // <-- REQUIRED parameter
"icon_emoji": ":robot_face:", // <-- optional parameter, use if you want to override default icon,
//"icon_url" : "http://image" // <-- optional parameter, use if you want to override default icon
}
sendToSlack_(url,payload)
}
function sendToSlack_(url,payload) {
var options = {
"method" : "post",
"contentType" : "application/json",
"payload" : JSON.stringify(payload)
};
return UrlFetchApp.fetch(url, options)
}
From your question, this sample supposes as follows.
When a file is uploaded within the last 24 hours, it updates my google sheet (perfect).
Then it messages the designated slack channel (perfect).
For Question 1
Reason :
At your script, when it runs, it posts to Slack every time. So even if there no updated files, "undefined" is posted.
Solution :
Using if (updatedfiles.length > 0) {, it posts at only when there are update file.
In this case, when there is no update, the data is not posted.
For Question 2
Reason :
fileName and fileURL created at while loop are variable each other. So only last file in files retrieved by searchFiles() is used for posting.
Solution :
Using var updatedfiles = [];, it imports information of updated files to the array. it post them using "attachments": updatedfiles as the payload.
Help document for attachments is https://api.slack.com/docs/message-attachments.
Modified Script
The modified script is as follows. In my environment, this payload of script works fine. But if this doesn't work at your environment, it may require a debug.
var updatedfiles = []; // <--- ##Added
while( files.hasNext() ) {
var file = files.next();
var fileName = file.getName();
var fileURL = file.getUrl();
var lastUpdated = Utilities.formatDate(file.getLastUpdated(), timezone, "yyyy-MM-dd HH:mm");
var dateCreated = Utilities.formatDate(file.getDateCreated(), timezone, "yyyy-MM-dd HH:mm")
row += "<li>" + lastUpdated + " <a href='" + fileURL + "'>" + fileName + "</a></li>";
sheet.appendRow([dateCreated, lastUpdated, fileName, fileURL]);
count++;
updatedfiles.push({"text": "Upload! *''"+fileName+"''* "+ fileURL}); // <--- ##Added
}
if (updatedfiles.length > 0) { // <--- ##Added
var url = "https://hooks.slack.com/services/XXXX";
var payload = {
"channel" : channel,
"username" : "DriveUpload",
"attachments": updatedfiles, // <--- ##Added
"icon_emoji": ":robot_face:",
}
sendToSlack_(url, payload);
}
It modified checkForChangedFiles(). Script above var row = "", count=0; has not been modified. So above script shows only the modified part. It showed the modified parts as <--- ##Added.
If I misunderstand your questions, I'm sorry.