Deleting past / old events from all calendars - google-apps-script

So I'm trying to delete all events that are older than a month from my calendars.
This script goes halfway; the logger prints out the name of the first event, but then I receive this error:
Exception: Action not allowed
deleteOldEvents # deleteOldEvents.gs.gs:13
The error points to ev.deleteEvent(); so something went wrong there.
Here is the full script:
function deleteOldEvents() {
var fromDate = new Date(2013, 0, 1, 0, 0, 0);
var lastMonth = new Date()
lastMonth.setMonth(lastMonth.getMonth() - 1)
var calendars = CalendarApp.getAllCalendars();
for (var i = 0; i < calendars.length; i++) {
var calendar = calendars[i];
var events = calendar.getEvents(fromDate, lastMonth);
for (var j = 0; j < events.length; j++) {
var ev = events[i];
Logger.log(ev.getTitle()); // show event name in log
ev.deleteEvent();
}
}
}

Modification points:
I think that var ev = events[i]; should be var ev = events[j];.
CalendarApp.getAllCalendars() returns all calendars. In this case, the calendars of "contacts" and "holiday" are also included in the returned calendars. From your error message of Exception: Action not allowed, I guessed that the reason for your current issue might be due to this.
When these points are reflected in your script, how about the following modification?
Modified script:
function deleteOldEvents() {
var fromDate = new Date(2013, 0, 1, 0, 0, 0);
var lastMonth = new Date()
lastMonth.setMonth(lastMonth.getMonth() - 1)
var calendars = CalendarApp.getAllCalendars();
for (var i = 0; i < calendars.length; i++) {
var calendar = calendars[i];
var id = calendar.getId();
if (!["#contacts", "#holiday"].some(e => id.includes(e))) {
var events = calendar.getEvents(fromDate, lastMonth);
for (var j = 0; j < events.length; j++) {
var ev = events[j];
Logger.log(ev.getTitle()); // show event name in log
ev.deleteEvent();
}
}
}
}
For example, CalendarApp.getAllCalendars() includes the other user's calendars and you have no writer permission, how about modifying the above script as follows?
From
if (!["#contacts", "#holiday"].some(e => id.includes(e))) {
To
if (!["#contacts", "#holiday"].some(e => id.includes(e)) && calendar.isOwnedByMe()) {
Or
From
var events = calendar.getEvents(fromDate, lastMonth);
for (var j = 0; j < events.length; j++) {
var ev = events[j];
Logger.log(ev.getTitle()); // show event name in log
ev.deleteEvent();
}
To
try {
var events = calendar.getEvents(fromDate, lastMonth);
for (var j = 0; j < events.length; j++) {
var ev = events[j];
Logger.log(ev.getTitle()); // show event name in log
ev.deleteEvent();
}
} catch(e) {
console.log(e.message);
}
Or, use just try-cath as follows.
function deleteOldEvents() {
var fromDate = new Date(2013, 0, 1, 0, 0, 0);
var lastMonth = new Date()
lastMonth.setMonth(lastMonth.getMonth() - 1)
var calendars = CalendarApp.getAllCalendars();
for (var i = 0; i < calendars.length; i++) {
var calendar = calendars[i];
try {
var events = calendar.getEvents(fromDate, lastMonth);
for (var j = 0; j < events.length; j++) {
var ev = events[j];
Logger.log(ev.getTitle()); // show event name in log
ev.deleteEvent();
}
} catch(e) {
console.log(e.message);
}
}
}
Reference:
getAllCalendars()
Added:
If the number of events is large, I'm worried that the process cost might become high. If you want to reduce the process cost of the script, as another direction, how about the following sample script?
In this case, the batch requests method is used for deleting the events. So, before you run this script, please do the following flow.
1. Install a Google Apps Script library.
In this sample script, batch requests are used. But, the script for requesting batch requests might be a bit complicated. So, in this sample script, a Google Apps Script library is used.
Please install a Google Apps Script library of "BatchRequest". Ref You can see how to install it at here.
2. Enable Calendar API.
Please enable Calendar API at Advanced Google services.
3. Sample script:
function myFunction() {
// Retrieve calendar IDs of calendars with the owner or writer.
const calendarIds = Calendar.CalendarList.list({ maxResults: 250, minAccessRole: "writer" }).items.map(({ id }) => id);
// or const calendarIds = ["###calendarId of your sample Calendar###"];
// Search all events older than 1 month
const max = new Date();
max.setMonth(max.getMonth() - 1);
const timeMax = max.toISOString();
calendarIds.forEach(calendarId => {
const requests = Calendar.Events.list(calendarId, { maxResults: 2500, timeZone: "UTC", timeMax }).items.map(({ id }) => ({
method: "DELETE",
endpoint: `https://www.googleapis.com/calendar/v3/calendars/${calendarId}/events/${id}`,
}));
const len = requests.length;
// Delete searched events.
if (len > 0) {
console.log(`${len} items are deleted.`);
BatchRequest.EDo({ batchPath: "batch/calendar/v3", requests });
console.log(`"${calendarId}" was done.`);
} else {
console.log(`"${calendarId}" has no delete events.`);
}
});
}
In this sample script, from your showing script, for the Google Calendars that you have permission for writing, all events older than 1 month are retrieved. And, those events are deleted.
IMPORTANT:
When this script is run, the events are deleted. So, please be careful about this. I would like to recommend testing this script using a sample Calendar. At that time, please modify const calendars = Calendar.CalendarList.list({ maxResults: 250, minAccessRole: "writer" }).items.map(({ id }) => id); to const calendarIds = ["###calendarId of your sample Calendar###"];, and test it. By this, the specific calendar is used.
In this sample script, as a simple script, pageToken is not used. So, the maximum number of Calendars is 250 and the maximum number of events is 2500. If your actual situation is more than theirs, please use pageToken. By this, all events from all Calendars can be retrieved.

Deleet Events occuring between 100 years ago and 1 month ago:
function deleteEventsOlderThanNMonths(n=1) {
let dt = new Date();
let edt = new Date(dt.getFullYear(),dt.getMonth() - n,dt.getDate());//a months ago
let sdt = new Date(dt.getFullYear() -100,dt.getMonth(),dt.getDate());//100 years ago
let cal = CalendarApp.getCalendarById(calid);
let evs = cal.getEvents(sdt,edt);
evs.forEach(ev => {ev.deleteEvent()})
}

Related

How can Google Sheets Form Update Records from Results Using Google App script?

I have a program that filters and updates data from an existing sheet.
The program works as follows:
1. Find and filter out the required value
2. Enter data in [Adjustment] column then update to database in Record sheet.
I tried to try but my program doesn't seem to work.
I tried to edit the program code but when run it will affect the other columns and the [adjustment] column value is entered wrong.
This is my link program
function Searchold(){
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var shtRecords = ss. getSheetByName ("RECORD");
var shtForm = ss. getSheetByName ("TEST") ;
var records = shtRecords. getDataRange () . getValues ();
var sField = shtForm. getRange ("A3").getValue ();
var sValue = shtForm.getRange ("A6").getValue();
var sCol = records [0].lastIndexOf(sField);
var results = records.filter(function(e){return sValue == e[sCol] });
if(results.length==0){SpreadsheetApp.getUi().alert("not found values");}
else{
shtForm.getRange(9,1,results.length,results[0].length).setValues(results);
}
}
function Updatenew(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var shtRecords = ss.getSheetByName("RECORD");
var shtForm = ss.getSheetByName("TEST");
var LastRow = shtForm.getRange("A8").getNextDataCell(SpreadsheetApp.Direction.DOWN).getLastRow();
var newData = shtForm.getRange(9,1,LastRow -1,7).getValues();
for(var i =0; i<newData.length;i++){
var oldData= shtRecords.getDataRange().getValues();
for(var j= 0;j<oldData.length;j++){
if(newData[i][0] ==oldData[j][0]){
var newData2 = [newData[i]];
shtRecords.getRange(j + 1,1,1,newData2[0].length).setValues(newData2);
}
}
}
}
Can you help me with the update program? Sincerely thank you
Modification points:
When I saw your showing script of Updatenew, I think that each row of var oldData = shtRecords.getDataRange().getValues() is used in each loop of for (var i = 0; i < newData.length; i++) {}. By this, each row is overwritten by each row of newData. By this, all searched rows in "RECORD" sheet are the same value. I thought that this might be the reason for your issue.
var oldData = shtRecords.getDataRange().getValues(); can be used one call.
In order to avoid this issue by modifying your script, as one of several methods, how about the following modification?
From:
for (var i = 0; i < newData.length; i++) {
var oldData = shtRecords.getDataRange().getValues();
for (var j = 0; j < oldData.length; j++) {
if (newData[i][0] == oldData[j][0]) {
var newData2 = [newData[i]];
shtRecords.getRange(j + 1, 1, 1, newData2[0].length).setValues(newData2);
}
}
}
To:
var oldData = shtRecords.getDataRange().getValues();
for (var j = 0; j < oldData.length; j++) {
for (var i = 0; i < newData.length; i++) {
if (newData[0][0] == oldData[j][0]) {
var newData2 = newData.splice(0, 1);
shtRecords.getRange(j + 1, 1, 1, newData2[0].length).setValues(newData2);
break;
}
}
}
Note:
At the above modification, setValues is used in a loop. In this case, the process cost becomes high. If you want to reduce the process cost of the script, how about using Sheets API? When Sheets API is used, how about the following modification? Please enable Sheets API at Advanced Google services.
To
var temp = newData.slice();
var data = shtRecords.getDataRange().getValues().reduce((ar, r, i) => {
if (temp[0][0] == r[0]) {
var t = temp.splice(0, 1);
t[0][2] = Utilities.formatDate(t[0][2], Session.getScriptTimeZone(), "dd/MM/yyyy");
t[0][4] = Utilities.formatDate(t[0][4], Session.getScriptTimeZone(), "dd/MM/yyyy");
ar.push({ range: `'RECORD'!A${i + 1}`, values: t });
}
return ar;
}, []);
Sheets.Spreadsheets.Values.batchUpdate({ data, valueInputOption: "USER_ENTERED" }, ss.getId());

Filter gmails using timestamp

Am new dabbling with Google Apps Script; would like to ask if I'm in the right direction, and how to I manipulate time within the script.
I'm struggling in trying to maniuplate time values in Google App Script, basically I am able to pull the timestamp of each email sent, but I only want to paste into the spreadsheet email information that were recent, e.g. within 30minutes from script run time. This is to avoid pulling duplicate information.
Not sure if there is a currentTime() function here, or I have to create a new Date() object and do some calculations from there. Tried a few variations and nothing seemed to work proper.
Would appreciate any help in getting towards the right direction in doing this thank you!
function getDetails(){
var DEST_URL = "SHEET_URL"; //redacted for sensitivity
var DEST_SHEETNAME = "Test";
var destss = SpreadsheetApp.openByUrl(DEST_URL);
var destSheet = destss.getSheetByName(DEST_SHEETNAME);
var threads = GmailApp.search("FILTERS"); //filter settings redacted for sensitivity
for(var i = 0; i < threads.length; i++){
var messages=threads[i].getMessages();
for(var j =0; j < 1; j++){ //only take first message in thread
var message = messages[j];
var subject = message.getSubject() ;
var sentTimeStamp = message.getDate();
if(sentTimeStamp is within last 30minutes as of script run time){ //this is where i need help
var delimitString = subject.split("is sent");
var detailName = delimitString[0];
var lastRow = destSheet.getLastRow();
destSheet.getRange(lastRow + 1,1).setValue(detailName);
destSheet.getRange(lastRow + 1,2),setValue(sentTimeStamp);
}
}
}
}
You can convert timeStamp into ms seconds and then compare to the value of "30 s ago"
Sample:
var sentTimeStamp = message.getDate();
var now = new Date();
var ThirtyMinutesAgo = now-30*60*1000;
if(sentTimeStamp.getTime() < ThirtyMinutesAgo){
...
}
References:
newDate()
getTime()
Another idea would be to query for emails that you received the last 30 minutes.
Explanation:
You can get the emails that you received the last 30 minutes ago as a query in the GmailApp.search function. See this link to see what filters you can use.
This will get the last emails with keyword "FILTERS" that you received the last 30 minutes.
var ThirtyMinutesAgo = new Date();
ThirtyMinutesAgo.setMinutes(ThirtyMinutesAgo.getMinutes() - 30);
const queryString = `"FILTERS" newer:${Math.round(ThirtyMinutesAgo.getTime()/1000)}`
const threads = GmailApp.search(queryString); // threads the last 30 minutes
This approach is more efficient for two reasons:
You have less data (threads) to iterate over with the for loop.
You don't need to apply and if statement on every thread.
Solution:
function getDetails(){
var DEST_URL = "SHEET_URL"; //redacted for sensitivity
var DEST_SHEETNAME = "Test";
var destss = SpreadsheetApp.openByUrl(DEST_URL);
var destSheet = destss.getSheetByName(DEST_SHEETNAME);
// var threads = GmailApp.search("FILTERS"); //filter settings redacted for sensitivity
// new code
var ThirtyMinutesAgo = new Date();
ThirtyMinutesAgo.setMinutes(ThirtyMinutesAgo.getMinutes() - 30);
const queryString = `"FILTERS" newer:${Math.round(ThirtyMinutesAgo.getTime()/1000)}`
const threads = GmailApp.search(queryString); // threads the last 30 minutes
//
for(var i = 0; i < threads.length; i++){
var messages=threads[i].getMessages();
for(var j =0; j < 1; j++){ //only take first message in thread
var message = messages[j];
var subject = message.getSubject() ;
var sentTimeStamp = message.getDate();
var delimitString = subject.split("is sent");
var detailName = delimitString[0];
var lastRow = destSheet.getLastRow();
destSheet.getRange(lastRow + 1,1).setValue(detailName);
destSheet.getRange(lastRow + 1,2),setValue(sentTimeStamp);
}
}
}
}

Permanently delete gmail message after some days

I'm using this script for deleting old messages from gmail every X days.
It functions correctly, however messages are sent to trash.
I want to delete the messages permanently without sending them to trash.
Someone can modify this script?
// The name of the Gmail Label that is to be autopurged?
var GMAIL_LABEL = "mylabel";
// Purge messages automatically after how many days?
var PURGE_AFTER = "21";
function purgeGmail() {
var age = new Date();
age.setDate(age.getDate() - PURGE_AFTER);
var purge = Utilities.formatDate(age, Session.getTimeZone(), "yyyy-MM-dd");
var search = "label:" + GMAIL_LABEL + " before:" + purge;
// This will create a simple Gmail search
// query like label:Newsletters before:10/12/2012
try {
// We are processing 100 messages in a batch to prevent script errors.
// Else it may throw Exceed Maximum Execution Time exception in Apps Script
var threads = GmailApp.search(search, 0, 100);
// For large batches, create another time-based trigger that will
// activate the auto-purge process after 'n' minutes.
// if (threads.length == 100) {
// ScriptApp.newTrigger("purgeGmail")
// .timeBased()
// .at(new Date((new Date()).getTime() + 1000*60*10))
// .create();
// }
// An email thread may have multiple messages and the timestamp of
// individual messages can be different.
for (var i=0; i<threads.length; i++) {
var messages = GmailApp.getMessagesForThread(threads[i]);
for (var j=0; j<messages.length; j++) {
var email = messages[j];
if (email.getDate() < age) {
email.moveToTrash();
}
}
}
// If the script fails for some reason or catches an exception,
// it will simply defer auto-purge until the next day.
} catch (e) {}
}
thanks
It's pretty simple all you have to do is get all of your message id's in an array and then use the following two lines.
var request={"ids":messageIdArray};
Gmail.Users.Messages.batchDelete(request, "me");
You will have to enable the Advanced Gmail API
batchDelete
So if the rest of your code actually works then this should do it:
var GMAIL_LABEL = "mylabel";
var PURGE_AFTER = "21";
function purgeGmail() {
var age = new Date();
age.setDate(age.getDate() - PURGE_AFTER);
var purge = Utilities.formatDate(age, Session.getTimeZone(), "yyyy-MM-dd");
var search = "label:" + GMAIL_LABEL + " before:" + purge;
try {
var msgA=[];
for (var i=0; i<threads.length; i++) {
var messages = GmailApp.getMessagesForThread(threads[i]);
for (var j=0; j<messages.length; j++) {
var email = messages[j];
if (email.getDate() < age) {
msgA.push(email.getId());
}
}
}
} catch (e) {}
}

Adding guest name to calendar using sheets/forms/forms.gs from Google Apps Scripts guide

I have very limited experience with Google Apps Scripts, but have successfully modified the code from https://developers.google.com/apps-script/quickstart/forms to meet almost all of my needs.
Need to know how to view on my calendar all guests who sign up for a date.
I have investigated CalendarApp options, but can't get anything formatted to work so that the name of anyone who completes the Google Form for a specific date shows up on my calendar on that date -- maybe as a guest or attendee?
var cal = CalendarApp.getOwnedCalendarById(XXX);
for (var i = 1; i < values.length; i++) {
var session = values[i];
var title = session[0];
var start = joinDateAndTime_(session[1], session[2]);
var end = joinDateAndTime_(session[1], session[3]);
var options = {location: session[4], sendInvites: true};
var event = cal.createEvent(title, start, end, options)
.setGuestsCanSeeGuests(false);
session[5] = event.getId();
}
range.setValues(values);
var schedule = {};
for (var i = 1; i < values.length; i++) {
var session = values[i];
var day = session[1].toLocaleDateString();
var time = session[2].toLocaleTimeString();
if (!schedule[day]) {
schedule[day] = {};
}
if (!schedule[day][time]) {
schedule[day][time] = [];
}
schedule[day][time].push(session[0]);
}
// Create the form and add a multiple-choice question for each timeslot.
var form = FormApp.create('2019-2020 Semester 1 Sign up');
form.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());
form.addTextItem().setTitle('Name').setRequired(true);
form.addTextItem().setTitle('Email').setRequired(true);
form.addTextItem().setTitle('Phone Number').setRequired(false);
for (var day in schedule) {
var header = form.addSectionHeaderItem().setTitle('Odysseys on ' + day);
for (var time in schedule[day]) {
var item = form.addMultipleChoiceItem().setTitle(time + ' ' + day)
.setChoiceValues(schedule[day][time]);
}
}
}
I see you want to make sure your events have the right guests added to them. After reading the comments I can see that Altigraph put you on the right track. Below is the syntax you need (I’m assuming that “values” refers to a user’s particular response to the form):
For adding guests in the createEvent() options:
for (var i = 1; i < values.length; i++) {
var session = values[i];
var title = session[0];
var start = joinDateAndTime_(session[1], session[2]);
var end = joinDateAndTime_(session[1], session[3]);
var options = {location: session[4], sendInvites: true,
guests: email,
};
var event = cal.createEvent(title, start, end, options)
session[5] = event.getId();
}
This email you can request from the user in the form, like you get their name or their phone, if you use a Google Form directly, there is the option to use this method to get the user’s email.
Alternatively, to use addGuest(), once you have the calendar created (i.e. the “event” object you have at the end of this snippet) you would simply use event.addGuest(email); where email is the same as in the other option. The documentation for it is at this link

Reading a named calendar's events

I am trying to write a script that deletes all the events from every calendar in a certain month. Based on this method it looks like I am using the correct code, but every time I try to run this code, I get the error,
TypeError: Cannot find function getEvents in object Calendar. (line 17, file "Code")
My code is:
function myFunction() {
var year = 2018;
var month = 11;
var fromDate = new Date(year,month,1,0,0,0);
var toDate = new Date(year,month,28,0,0,0);
var calendars = ['def', 'Appointments', 'Chores', 'Contacts', 'Dates', 'Errand', 'IMPORTANT!!', 'Scheduling','Time-Sensitive', 'TV Show', 'Work'];
for (var x = 0; x < calendars.length; x++) {
var calendar = CalendarApp.getCalendarsByName(calendars[x])[0];
var events = calendar.getEvents(fromDate, toDate);
for (var i = 0; i < events.length; i++) {
var ev = events[i];
ev.deleteEvent();
}
}
}
Does anyone know what I am doing wrong?
The problem is that CalendarApp.getCalendarsByName(calendars[x]); returns an array. To access even the first and only element in the array you can do this:
var calendar=CalendarApp.getCalendarsByName(calendars[x])[0]; so that now the variable calendar is now a calendar and not an array of calendars.
Here's the form the script script I'm using:
function myFunction101() {
var year=2018;
var month=11;
var fromDate=new Date(year,month,1,0,0,0);
var toDate=new Date(year,month,28,0,0,0);
var calendars=['', ''];//using my calendars
for(var x=0;x<calendars.length;x++) {
var calendar=CalendarApp.getCalendarsByName(calendars[x])[0];
var events=calendar.getEvents(fromDate, toDate);
for(var i = 0; i < events.length; i++){
var ev = events[i];
Logger.log(ev.getTitle());
//ev.deleteEvent();
}
}
}