问题
在 Google 日历中创建事件时执行更新电子表格的函数。
解决方案
使用 EventUpdated 可安装触发器,每次在日历中修改事件(例如创建、更新或删除 - 请参阅参考资料)时都会触发该触发器。从那里,您可以采用简单的方式(使用内置的 CalendarApp 类更新电子表格中的所有数据)或困难的方式(更新通过增量同步更改的数据 - 请参阅官方指南)。
第 0 部分 - 安装触发器
/**
* Installs Calendar trigger;
*/
function calendarTrigger() {
var trigger = ScriptApp.newTrigger('callback name here')
.forUserCalendar('calendar owners email here')
.onEventUpdated()
.create();
}
第 1 部分 - 回调(日历 -> 电子表格)
/**
* Updates spreadsheet;
* @param {Object} e event object;
*/
function updateSpreadsheet(e) {
//access spreadsheet;
var ss = SpreadsheetApp.openById('target spreadsheet id');
var sh = ss.getSheetByName('target sheet name');
var datarng = sh.getDataRange(); //assumed that data is only calendar data;
//access calendar;
var calendar = CalendarApp.getCalendarById(e.calendarId);
//set timeframes;
var start = new Date();
var end =new Date();
//get year before and three after;
start.setFullYear(start.getFullYear()-1);
end.setFullYear(end.getFullYear()+3);
//get events;
var events = calendar.getEvents(start, end);
//map events Array to a two-dimensional array of values;
events = events.map(function(event){
return [event.getTitle(),event.getDescription(),event.getStartTime(),event.getEndTime()];
});
//clear values;
datarng.clear();
//setup range;
var rng = sh.getRange(1,1, events.length, events[0].length);
//apply changes;
rng.setValues(events);
}
备注
- 根据 Tanaike 的评论 - 如果事件是通过脚本或请求触发的(请参阅限制参考),请务必考虑到 不触发 的触发器(简单和可安装)。要启用此类功能,您必须引入 polling 或与 WebApp 捆绑,该脚本将在创建事件后调用(请参阅下面的捆绑示例)。
- 您的解决方案更适合反向流程:在电子表格中编辑 -> 在日历中编辑(如果您修改它以在日历上执行操作而不是更新电子表格,ofc)。
- 利用
Date 内置对象的方法,如getFullYear()(其他方法请参见参考资料),使您的代码更灵活、更易于理解。顺便说一句,我会将“一天中的毫秒数”数据存储为常量 (86400000)。
- 永远不要在循环中使用
getRange()、getValue()、setValue() 和类似的方法(并且通常尽可能少地调用它们) - 它们是 I/O 方法,因此速度很慢(您可以自己查看)通过尝试在循环中写入 >200 行)。在开始时获取所需的范围/值,执行修改并批量写入(例如使用setValues() 方法)。
参考
-
EventUpdated事件reference;
- 日历增量同步guide;
-
Date 内置对象reference;
-
setValues()方法reference;
-
Using batch Google Apps 脚本中的操作;
-
Installable 和 simple 触发限制;
WebApp 捆绑
第 0 部分 - 先决条件
如果您想通过脚本执行来创建/更新/删除日历事件,您可以将目标脚本与一个简单的 WebApp 捆绑在一起。您需要确保:
- WebApp 部署访问设置为
anyone, even anonymous(强烈建议引入某种形式的请求认证);
- WebApp 代码有 一个 函数,名为
doPost 接受事件对象(通常命名为 e,但由您决定)作为单个参数。
第 1 部分 - 构建 WebApp
此构建假定所有修改都在 WebApp 中进行,但您可以,例如,返回回调名称以在成功请求时运行并在调用脚本中处理更新。由于在上面的回调中只使用了事件对象的calendarId 属性,我们可以向它传递一个只有这个属性集的自定义对象:
/**
* Callback for POST requests (always called "doPost");
* @param {Object} e event object;
* @return {Object} TextOutput;
*/
function doPost(e) {
//access request params;
var body = JSON.parse(e.postData.contents);
//access calendar id;
var calendarId = body.calendar;
if(calendarId) {
updateSpreadsheet({calendarId:calendarId}); //callback;
return ContentService.createTextOutput('Success');
}else {
return ContentService.createTextOutput('Invalid request');
}
}
第 2 部分 - 示例调用脚本
此构建假定调用脚本和 WebApp 是同一个脚本项目(因此可以通过 ScriptApp.getService().getUrl() 访问其 Url,否则粘贴在 WebApp 部署期间提供给您的 URL)。构建需要熟悉UrlFetchApp(请参阅reference)。
/**
* Creates event;
*/
function createEvent() {
var calendar = CalendarApp.getCalendarById('your calendar id here');
//modify whatever you need to (this build creates a simple event);
calendar.createEvent('TEST AUTO', new Date(), new Date());
//construct request parameters;
var params = {
method: 'post',
contentType: 'application/json',
muteHttpExceptions: true,
payload: JSON.stringify({
calendar: calendar.getId()
})
};
//send request and handle result;
var updated = UrlFetchApp.fetch(ScriptApp.getService().getUrl(),params);
Logger.log(updated); //should log "Success";
}