Modify a Google Calendar recurrence in Apps Script - google-apps-script

I'm trying to create a script that, given a date, will change the end date of all my recurrent events this week to that date. Unfortunately, I can't find a way to update a recurrence info.
First, I iterate all events in the week. Then, thanks to the API, I can get the recurrence info of every event:
var eventId = event.getId().split("#")[0];
var eventFromAPI = Calendar.Events.get(ID_CALENDARIO_MENUS, eventId);
var cadenaRecurrencia = eventFromAPI["recurrence"];
witch then I use to create a new recurrence to overwrite the existing one in the event.
var recurrencia = CalendarApp.newRecurrence().addWeeklyRule().onlyOnWeekdays(arrayWeekdays).until(fecha);
But when I try to set the new recurrence, it asks me for a start date that should be the original, and I can't seem to retrieve that data:
var serie = calendario_menus.getEventSeriesById(eventoId);
serie.setRecurrence(recurrencia, date);
Is there a way to access an event's recurrence, modify it so it changes for all the events that use the recurrence and update it?
Thanks for your help.

Related

How to update event time in Google calendar from Google sheet by event name

I have a number of Futsal(indoor soccer) teams and am trying to make managing the calendar really easy.
Currently I pull the game details into a spreadsheet using an IMPORTHTML function with a query attached, then sort the data to process either by team or can do all teams at once.
All import sheets are set up with the same configuration to keep it simple.
Calendar ID is in B3
Start date and time is from B5:B100
End date and time is from C5:C100
The game name title from D5:D100 (displayed like "Cunning Stunts ✰✰ vs Borussia Dropkicks ☆✰")
Very rarely does the team change however the times can be adjusted throughout the season and I'd like to run the script and update the appointment time.
I can import games into the calendar without any problem as I've worked that bit out but cannot get my head around changing the time of an entry based on the event name.
I have tried a large amount of trial and error using other people questions around getId() functions and also if/else but I can only create duplicate events.
I'm fairly fresh to this world but am eager to continue learning and don't give up easily so any help would be appreciated.
Here is what I have working so far.
function gameImport() {
var spreadsheet = SpreadsheetApp.getActiveSheet();
var calendarId = spreadsheet.getRange("b3").getValue();
var cal = CalendarApp.getCalendarById(calendarId);
var calendarName = cal.getName();
Browser.msgBox("Export Calendar = " + calendarName);
var games = spreadsheet.getRange("A5:D100").getValues();
for (x=0; x<games.length; x++) {
var details = games[x];
var startTime = new Date(details[1]); //Start date and time
var endTime = new Date(details[2]); //End date and time
var title = details[3]; //Game title
cal.createEvent(title,startTime,endTime);
Browser.msgBox("Great Sucess");
}
}

Alternative to using triggers to automatically send email for each calendar event created

I am trying to automate certain parts of my workflow for scheduling clients with Google Calendar. I've successfully managed to capture new/edited/deleted events in Google Apps Script using a trigger which detects changes and Calendar.event.list to sync those changes with a spreadsheet.
I create a new row, or edit an existing one, in my spreadsheet of all the clients. What I desire to do is three days before the appointment with the client, automatically generate a custom email with all of their details, to send them as a reminder regarding the appointment.
My plan was every time a new row was created in the Spreadsheet (when a new Calendar event was created), was to make a new email trigger. That trigger would execute code to create an email, with all of the clients info.
function createEmailTrigger(event) {
var today = new Date();
today.setHours(0,0,0,0); // Don't care about time
// Create Email Trigger three days before
const sendDaysBefore = 3;
var daysBefore = new Date(event.start);
daysBefore.setDate(daysBefore.getDate() - sendDaysBefore);
var trigger = ScriptApp.newTrigger('sendEmail')
.timeBased()
.at(daysBefore)
.create();
associateEventWithTrigger(trigger.getUniqueId(), event);
return trigger.getUniqueId();
}
associateEventWithTrigger connects the trigger id with the Calendar event. sendEmail would then create a new email with all of the client's info, which came from the Calendar event. When the trigger is executed, it deletes the trigger since it won't be used again.
All of this was working fine, as I was testing one Calendar event at a time. However, once I decided to sync all of this year's Calendar events, the script very quickly threw this error:
Exception: This script has too many triggers. Triggers must be deleted from the script before more can be added.
Apparently you can only have 20 triggers per user/script. This is very inconvenient, as I was expecting to create hundreds of triggers.
Therefore, I need to rethink how to go about doing this. Any suggestions? I appreciate it.
Proposed workaround
This script is designed to be run on a time-driven trigger that runs daily.
function sendReminderEmails() {
let file = SpreadsheetApp.getActive();
let sheet = file.getSheetByName("Sheet1");
let range = sheet.getDataRange();
let values = range.getValues();
// removing headers
values.shift()
values.forEach(row => {
let name = row[0]
let email = row[1]
let date = row[2]
// Date object representing time now
let now = new Date();
// helper variables
let second = 1000;
let minute = second * 60;
let hour = minute * 60;
let day = hour * 24;
// gets time to appointment in milliseconds
let timeToApp = date.getTime() - now.getTime()
if (timeToApp > 2 * day && timeToApp < 3 * day) {
MailApp.sendEmail(
email,
"Remember Your Appointment",
"Hello " + name + ",\nYou have an appointment coming up soon."
)
}
})
}
This is based on a sample spreadsheet like this:
So you would need to adapt it to your particular format.
Script walkthrough
It is based on the date object.
If your dates are stored as formatted dates in your spreadsheet, when you getValues, Apps Script will automatically pass them as Date object. Alternatively you can cast them as Date objects in Apps Script if needed.
The script starts off by getting all the values in the target sheet.
It initialized a new Date object that represents the time now.
It then goes through each row of the target sheet and gets a value for how long until the appointment.
If the value is between 2 days and 3 days, then an email is sent.
I Think you could make an script to search events every day , events that are 3 days ahead , select then and send email. So it will be just one script that will be triggeres every day, using the date trigger mode.

check if event exists before adding it from google sheet to calendar

I've got a script that updates a calendar from a sheet.
Is there a way of getting the eventID of an event i would add to the calendar if it didn't exist ?
what would be the ID of the event to be created with (title, start, stop)
I can then check it against the eventIDs of existing events and add , if not already there.
i think this would be quicker than checking existing events for matching all properties before either adding the new one , or just moving on
I don't think that the IDs work the way you seem to think they do (as some sort of hash of the information inside them). Can you check the date you want for the title you want? I think that would be enough.
this is a script to get you started, it finds the next occurence of "Sample Meeting" in the next 20 days. You can easily narrow the scope to a single day and then add an "else" that creates the new meeting.
function insertItem() {
var fromDate = new Date(); //This is Today
var toDate = new Date();
toDate.setDate(toDate.getDate()+20);
Logger.log("From "+fromDate+" to "+toDate);
var calendar = CalendarApp.getDefaultCalendar();
var events = calendar.getEvents(fromDate, toDate);
for(var i=0; i<events.length;i++)
{
var ev = events[i];
var title = calendar.getEventSeriesById(ev.getId()).getTitle();
if (title.indexOf("Sample Meeting")>-1){
var start = ev.getStartTime();
Logger.log("Found Team Meeting");
var id = ev.getId();
var date = ev.getStartTime();
var desc = ev.getDescription();
return;
}
}
}
It can be done, but its going to take a bit of work.
Event ids are auto-generated by default. However, you can generate custom ids for an event as long as they conform to Google's format requirements. So, you'll have to manage the generation of these event ids yourself.
You will have to ensure that the id is unique, so you might want to base it on a combination of inputs related to the event.
Once you have some way of generating a unique id, you can use it to fetch an event directly by id, but you have to use the Advanced Calendar Service (the built-in service does not allow lookup by event id, but it does allow lookup by iCalUID).

Google Apps Script: Calendar Service: Find first event in CalendarEventSeries

I'd like to calculate the age of a person whose birthday exists as event series within my calendar. To do this I need to know the first event within this series and that's the question: how to get the first event of a series from an actual event?
Thanks
Ronny
The other answer doesn't actually answer the initial request, the CalendarApp has no method to get the start date of a recurring event.
You should use the advanced Calendar API (must be enabled manually, see below and follow instructions)
Then use the advanced API to get the information you want, (the auto complete feature works on these methods too so you can easily see what is available)
Test code below, note that event ID is different for the advanced Calendar API, you have to remove the part after '#'.
function createTestEvents() {
var recurrence = CalendarApp.newRecurrence().addWeeklyRule().times(10);
var testEvent = CalendarApp.getDefaultCalendar().createEventSeries('test event serie', new Date('2016/05/10'), new Date(new Date('2016/05/10').getTime()+12*3600000), recurrence);
var id = testEvent.getId();
Logger.log('Event Series ID: ' + id);
viewTestEvent(id)
}
function viewTestEvent(id){
var event= CalendarApp.getDefaultCalendar().getEventSeriesById(id);
var calId = CalendarApp.getDefaultCalendar().getId();
Logger.log('event title = '+event.getTitle());
var AdvanncedId = id.substring(0,id.indexOf('#'));
Logger.log('AdvanncedId = '+AdvanncedId);
var testEvent = Calendar.Events.get(calId, AdvanncedId);
Logger.log('testEvent start = '+ testEvent.start);
return testEvent;
}
function test(){ // a few examples of what you can retrieve...
var event = viewTestEvent('59buf7nq6nr6qo79bh14kmsr6g#google.com');
Logger.log('\n\nstart = '+event.start);
Logger.log('\n\ncreated on = '+event.created);
Logger.log('\n\nend on = '+event.end);
Logger.log('\n\nrecurrence = '+event.recurrence);
}
You need to use the startDate parameter to get the date of the first event in the series (only the day is used; the time is ignored).
var eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries('No Meetings',
new Date('January 2, 2013 03:00:00 PM EST'),
CalendarApp.newRecurrence().addWeeklyRule()
.onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)
.until(new Date('January 1, 2014')));
Logger.log('Event Series ID: ' + eventSeries.getId());
You can also get the event series with the given ID using getEventSeriesById(iCalId).
If the ID given is for a single CalendarEvent, then a CalendarEventSeries will be returned with a single event in the series. Note that if the event series belongs to a calendar other than the default calendar, this method must be called from that Calendar; calling CalendarApp.getEventSeriesById(id) directly will only return an event series that exists in the default calendar.
Hope this helps!

Inviting guests to a "Quick Add" Google Calendar event

I am trying to improve the functionality of the "Quick Add" feature in Google Calendar. Quick Add is the feature that is normally accessed by going to Google Calendar and clicking the down arrow next to the red "Create" button (see image: http://s2.postimg.org/95zxshivt/calendar_screenshot.png).
The functionality I am trying to achieve is to allow the user to invite guests to the newly created Calendar event using keywords in what they type in the Quick Add box. For example, if the user uses the Quick Add box to add an event by entering the text Eat pizza tomorrow at 3pm with michelle#gmail.com and john#gmail.com, Google Calendar adds a new event with the title Eat pizza with michelle#gmail.com and john#gmail.com at 3pm the next day, like it is supposed to. I want to go a step further by having Google also send out two Calendar invites to the newly created event: one for michelle#gmail.com and the other to john#gmail.com.
I appreciate your advice on this topic. I am trying to understand what the best approach is:
Use a trigger with Google Apps Script to catch when the user has added a Calendar event. The trigger will access the title of the event, pick out any e-mail addresses present in the title, and send an invitation to the newly created event to each of those e-mail addresses.
Use a Chrome extension to have the user enter the string that they normally would type into the Quick Add box by clicking on the extension's icon in the Chrome browser. The extension would pick the e-mail addresses out of what the user types in, use createEventFromDescription(description) on the user's input to create the event, and then send an invitation to the newly created event to each of those e-mail addresses.
Please let me know what you think. I would greatly appreciate your ideas.
Lucy
As you probably know, there is not trigger source linked to the creation of an event from the Calendar Ui.
You have indeed 2 possibilities :
encode the event using a dedicated Ui (a chrome extension or a standalone webapp) that would take care of sending the invitations but that would probably not meet the initial requirement you described as "expanding the capabilities os the quickAdd features"
Find a way to detect an event creation and automatically send invitations from its content.
This last possibility is perfectly doable using a timer trigger that monitors your calendar and detects any new event (by comparing a list stored somewhere to the actual calendar content).
I have made such an app for a different purpose and it works nicely but there are a few difficulties you should be aware of.
When storing the events in scriptProperties (it's probably the best place to go) you have a limited amount of storage size available so you must know how much event you will be able to handle.
Any change in an event like adding a detail or rectifying a typo will re-trigger the invitation process and send the invitation again. Although this can probably be avoided but it would be quite complex.
When the script runs right after an event end, the comparison detects an change because one event is missing (from the script pov) so it could send a mail to cancel the event (if you had chosen to implement that functionality of course but I guess it's a "must have"). It might be a bit tricky to handle that situation... (compare event end time to actual time when the trigger fires the script, could be a matter of milliseconds ;-).
Apart from these difficulties, the general idea is as follow :
create a timer trigger to run every hour or so
store every event in this calendar in script Properties (or eventually in a spreadsheet) starting from the present date and ending in a few days (not too far ahead because it wouldn't make sense to send invites for an event happening next year)
compare the list with the calendar content
grab every "new" events and extract email address from the description (using regex for example or string manipulation)
send the invitations (that's the easy part)
This workflow works but it might be a bit fragile in the comparison and in the email detection.
EDIT
Since this was an interesting subject (IMHO) and that I thought I could use efficiently (now that I switched to english in my calendar UI ;-D) I wrote a code to achieve it...
I embedded the code in a spreadsheet to simplify the processsing and the storage of the events in the many calendars I own.
This spreadsheet is viewable here and if you make a copy of it you'll be able to run the code.
I setup a timer trigger that runs the autoCheckAllCal function every hour and it seems to work without any issue.
Tell me what you think.
The full code is reproduced below, it gets data from the calendar, checks if the event has guests and if not it checks the title for any valid email address (one or more) and sends the invitations automatically.
I used a regex to extract emails from the title string (this regex was borrowed from an answer on SO since I'm not good enough at this !)
note : setup an onOpen trigger for myOnOpen (because of global var declaration using SS service)
// update the ID below to your copy ID and run the Callist() function to get the calendars ID on first sheet.
//set up an onOpen trigger for the myOnOpen function
var ss = SpreadsheetApp.openById('1xDOaoSl3HbkS95cj8Jl-82rdiui7G0sFz96PIO6iVF4');// this spreadsheet
var calNamesSheet = ss.getSheetByName('calNames');
var calList = calNamesSheet.getDataRange().getValues();
function MyOnOpen() {
var menuEntries = [ {name: "Lauch autoTest", functionName: "autoCheckAllCals"},
{name: "delete created sheets", functionName: "delsheets"}
];
ss.addMenu("Tracking utilities",menuEntries);//
}
function autoCheckAllCals(){
var today = new Date(); // now
var startDate = new Date(today.setHours(0,0,0,0));// today # 0 AM
var endDate = new Date(new Date(startDate).setDate(startDate.getDate()+7)); // adjust time frame to read here = 7 days
for(var nn=0;nn<calList.length;nn++){
var logArray = new Array();
logArray.push(['Calendar + Title','Description','Start','End','Location','Creators','Date Created','Duration','Guests']);
var calName = calList[nn][0];
var calId = calList[nn][1];
var Calendar = CalendarApp.getCalendarById(calId);
var events = Calendar.getEvents(startDate , endDate);
if (events[0]) {
for (var i = 0; i < events.length; i++) {
var row = new Array();
row.push(calName +' : '+events[i].getTitle());
row.push(events[i].getDescription());
row.push(Utilities.formatDate(events[i].getStartTime(), Session.getScriptTimeZone(), "MMM-dd-yy")+' # ' +Utilities.formatDate(events[i].getStartTime(), Session.getScriptTimeZone(), "HH:mm"));
row.push(Utilities.formatDate(events[i].getEndTime(), Session.getScriptTimeZone(), "MMM-dd-yy")+' # ' +Utilities.formatDate(events[i].getEndTime(), Session.getScriptTimeZone(), "HH:mm"));
row.push(events[i].getLocation());
row.push(events[i].getCreators().join());
row.push('on '+Utilities.formatDate(events[i].getLastUpdated(), Session.getScriptTimeZone(), "MMM-dd-yyyy"));
row.push(((events[i].getEndTime() - events[i].getStartTime()) / 3600000)+' hours');//duration
var inviteList = checkInvites(events[i]);
if (inviteList.length==0){ // if guests were found in checkInvites() then don't read it from event since checkInvites() added them to the cal but this event is not yet updated
var list = events[i].getGuestList();
for(n=0;n<list.length;++n){inviteList.push(list[n].getEmail())};
}else{
for(var n in inviteList){
events[i].addGuest(inviteList[n]);
}
}
row.push(inviteList.join(', '));
logArray.push(row);
}
}
// Logger.log(logArray);
if(logArray.length==0){continue};
try{
var sheetToWrite = ss.insertSheet(calName,ss.getNumSheets());// create sheet if doesn't exist
}catch(err){
var sheetToWrite = ss.getSheetByName(calName);// else open it
}
sheetToWrite.getRange(1,1,logArray.length,logArray[0].length).setValues(logArray).setHorizontalAlignment('left'); // enhance formating
sheetToWrite.getRange(1,1,1,logArray[0].length).setBackground('#EEA').setBorder(true,true,true,true,true,true).setHorizontalAlignment('left').setFontSize(12);
for(var w in logArray[0]){
sheetToWrite.setColumnWidth(Number(w)+1,180);
}
}
}
function checkInvites(event){
var email = []
var title = event.getTitle();
if(title.indexOf('#')==-1){return email};
email = title.match(/([\w-\.]+)#((?:[\w]+\.)+)([a-zA-Z]{2,4})/g);
Logger.log('email var = '+email);
return email;
}
function delsheets(){
var numbofsheet = ss.getNumSheets();// check how many sheets in the spreadsheet
for (var pa=numbofsheet-1;pa>0;pa--){
ss.setActiveSheet(ss.getSheets()[pa]);
if(ss.getSheets()[pa].getSheetName()!='calNames'){
ss.deleteActiveSheet(); // delete sheets begining with the last one
Utilities.sleep(400);
}
}
SpreadsheetApp.flush();
}
// This small function is to get the list of calendar names & Ids that you have access to, please edit the calNames sheet to keep only the ones you want to monitor (without empty rows).
function Callist(){
calNamesSheet.getDataRange().clearContent();
var list = new Array();
var store = new Array();
list = CalendarApp.getAllCalendars()
for (n=0;n<list.length;++n){
var name = list[n].getName() ;
var id = list[n].getId() ;
store.push( [name,id])
}
calNamesSheet.getRange(1,1,store.length,store[0].length).setValues(store);
}
// Serge insas - 08-2014