Automatisch Google Calendar 触发器全解析:New calendar 与 New event 的轮询机制与源码实现
【免费下载链接】automatischThe open source Zapier alternative. Build workflow automation without spending time and money.项目地址: https://gitcode.com/GitHub_Trending/au/automatisch
Google Calendar 是 Automatisch(开源 Zapier 替代品)内置的日历自动化应用,本指南以官方文档 triggers.md 为骨架,系统讲解其提供的New calendar(新建日历)与New event(新建事件)两个触发器:从 Google Cloud OAuth 连接配置,到触发器的定义注册、分页拉取、去重与测试执行机制,并深入到 packages/backend/src/apps/google-calendar 的真实源码,带你掌握如何在流(Flow)中正确使用它们,以及它们底层"每 15 分钟轮询一次"的工作原理。读完本文,你将能够独立配置 Google Calendar 连接、理解触发器参数与去重行为,并能在自己的 Automatisch 实例上构建"当有新事件时发通知"一类的自动化流。
一、触发器总览:官方文档定义的两种触发场景
根据官方文档 triggers.md 的 frontmatter 元数据,Google Calendar 应用向用户开放两种触发器:
| 触发器名称 | 标识 Key | 官方描述 |
|---|---|---|
| New calendar | newCalendar | 当创建了一个新的日历时触发 |
| New event | newEvent | 当创建了一个新的事件时触发 |
在 Automatisch 文档站点中,这两个条目由items列表定义,通过 CustomListing.vue 组件渲染成可浏览的触发器清单页面。而在运行时,真正驱动自动化的是 triggers/index.js 导出的两个触发器实现:
import newCalendar from './new-calendar/index.js'; import newEvent from './new-event/index.js'; export default [newCalendar, newEvent];该数组被 应用主入口 index.js 挂载到应用定义上:
export default defineApp({ name: 'Google Calendar', key: 'google-calendar', baseUrl: 'https://calendar.google.com', apiBaseUrl: 'https://www.googleapis.com/calendar', iconUrl: '{BASE_URL}/apps/google-calendar/assets/favicon.svg', authDocUrl: '{DOCS_URL}/apps/google-calendar/connection', primaryColor: '#448AFF', supportsConnections: true, beforeRequest: [addAuthHeader], auth, triggers, dynamicData, });由此可见,两个触发器都是轮询型(polling)触发器,而非 Webhook 型。它们的共同特征是带有pollInterval属性,这一点在下一节会详细说明。
二、前置条件:配置 Google Calendar 连接(OAuth)
两个触发器都依赖用户授权的连接(Connection)。官方连接文档 connection.md 提供了完整的 Google Cloud 配置步骤,这里是完整继承并整理的实操流程:
- 前往Google Cloud Console创建一个项目(顶部项目下拉菜单 →New Project→ 填写项目名 →Create)。
- 进入API Library,搜索并启用Google Calendar API。
- 重复上述操作,再启用People API(People API 用于获取当前用户信息,与授权范围相关,见下文)。
- 进入OAuth consent screen,首次开发选择External(外部)类型以测试模式启动,点击Create。
- 填写App Name、User Support Email、Developer Contact Information,点击Save and Continue。
- 跳过 scopes 配置页,点击Save and Continue。
- 点击Add Users添加测试邮箱——当发布状态为 "Testing" 时,只有测试用户能访问应用。
- 点击Save and Continue完成同意屏幕配置。
- 进入Credentials,点击Create Credentials→ 选择OAuth client ID。
- 应用类型选择Web application并填写Name。
- 从 Automatisch 的 Google Calendar 连接表单中复制OAuth Redirect URL,粘贴到 Google 的Authorized redirect URIs字段,点击Create。
- 将弹出的Your Client ID填入 Automatisch 的
Client ID字段。 - 将弹出的Your Client Secret填入 Automatisch 的
Client Secret字段。 - 点击 Automatisch 上的Submit,连接即建立,之后即可在流中使用。
从源码 auth/index.js 可以看到,连接表单正是要求OAuth Redirect URL、Client ID、Client Secret三个字段,其中重定向 URL 由系统自动填充为{WEB_APP_URL}/app/google-calendar/connections/add,并标记为只读(readOnly: true)与可点击复制(clickToCopy: true),便于直接粘贴到 Google Cloud 控制台。
OAuth 授权范围(Scope)
两个触发器请求的 Google 授权范围定义在 common/auth-scope.js:
const authScope = [ 'https://www.googleapis.com/auth/calendar', 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', ];其中calendar范围赋予读取(及管理)日历与事件的权限,userinfo.email与userinfo.profile用于在验证凭据时获取当前用户身份信息(见common/get-current-user.js)。这也解释了为什么连接文档要求同时启用 People API。
三、触发器定义规范:为什么它们必须是"轮询型"
查看 helpers/define-trigger.js 的校验逻辑可以确认:任何触发器要么声明pollInterval(轮询型),要么声明type === 'webhook'(Webhook 型),否则会在注册时直接抛出异常:
const isWebhookOrPoll = triggerDefinition.pollInterval || triggerDefinition.type === 'webhook'; const isSchedulerTrigger = schedulerTriggers.includes(triggerDefinition.key); const isMcpTrigger = triggerDefinition.key === 'mcpTool'; const haveValidTriggerType = isWebhookOrPoll || isSchedulerTrigger || isMcpTrigger; if (!haveValidTriggerType) { throw new Error( `Trigger must have a poll interval or be a webhook for ${triggerDefinition.key}` ); }Google Calendar 的两个触发器均采用pollInterval: 15,即每 15 分钟由调度器触发一次轮询。这是理解其行为延迟的关键:新建事件后,触发器最多会在 15 分钟内被捕获,属于典型的近实时(near-real-time)方案,而非 Google Calendar 推送式实时回调。
四、New calendar 触发器源码解析
官方描述:Triggers when a new calendar is created(当创建新日历时触发)。完整实现位于 triggers/new-calendar/index.js:
export default defineTrigger({ name: 'New calendar', key: 'newCalendar', pollInterval: 15, description: 'Triggers when a new calendar is created.', arguments: [], async run($) { const params = { pageToken: undefined, maxResults: 250, }; do { const { data } = await $.http.get('/v3/users/me/calendarList', { params, }); params.pageToken = data.nextPageToken; if (data.items?.length) { for (const calendar of data.items.reverse()) { $.pushTriggerItem({ raw: calendar, meta: { internalId: calendar.etag, }, }); } } } while (params.pageToken); }, });值得注意的实现细节:
- 无需参数:
arguments为空数组,因为"当前用户的全部日历"由接口自动返回,不需要用户选择具体日历。 - 分页拉取:调用
GET /v3/users/me/calendarList获取当前用户的日历列表,每次最多 250 条(maxResults: 250),通过nextPageToken循环翻页直到取完所有页。 - 时间倒序输出:对当页数据执行
data.items.reverse(),使最新创建的日历排在最前,再由引擎按顺序处理。 - 去重依据 internalId:使用日历资源的
etag作为internalId。etag 是 Google API 资源的实体标签,同一日历的 etag 稳定不变,新建日历的 etag 则从未出现在历史记录中,因此天然适合作为"是否已处理过"的判定键。
五、New event 触发器源码解析
官方描述:Triggers when a new event is created(当创建新事件时触发)。完整实现位于 triggers/new-event/index.js:
export default defineTrigger({ name: 'New event', key: 'newEvent', pollInterval: 15, description: 'Triggers when a new event is created.', arguments: [ { label: 'Calendar', key: 'calendarId', type: 'dropdown', required: true, description: '', variables: false, source: { type: 'query', name: 'getDynamicData', arguments: [ { name: 'key', value: 'listCalendars', }, ], }, }, ], async run($) { const calendarId = $.step.parameters.calendarId; const params = { pageToken: undefined, orderBy: 'updated', }; do { const { data } = await $.http.get(`/v3/calendars/${calendarId}/events`, { params, }); params.pageToken = data.nextPageToken; if (data.items?.length) { for (const event of data.items.reverse()) { $.pushTriggerItem({ raw: event, meta: { internalId: event.etag, }, }); } } } while (params.pageToken); }, });它与 New calendar 的关键差异:
- 必须选择日历:声明了一个必填(
required: true)的Calendar下拉参数(calendarId),即触发范围被限定在用户指定的某个日历内。 - 下拉数据来自动态数据源:下拉框通过
getDynamicData查询listCalendars动态数据源填充选项。该数据源实现在 dynamic-data/list-calendars/index.js,它同样调用GET /v3/users/me/calendarList并分页收集所有日历,最终把calendar.id作为选项值、calendar.summary作为展示名称:
for (const calendar of data.items) { drives.data.push({ value: calendar.id, name: calendar.summary, }); }- 按更新时间排序:请求携带
orderBy: 'updated',让最近更新的事件排在前列,配合reverse()后,新创建(或新修改)的事件会被优先处理。注意这里"事件创建"在实现上体现为updated字段的排序——这是一个由源码得出的推断:只要事件的etag是新的,即便事件被修改也会被视为新的触发项(详见下一节的去重原理)。 - 接口路径带日历 ID:实际请求为
GET /v3/calendars/{calendarId}/events,其中calendarId来自步骤参数$.step.parameters.calendarId。
六、轮询调度、去重与测试执行:触发器运行时的幕后机制
两个触发器都调用$.pushTriggerItem()提交触发项,其运行时行为定义在 engine/global-variable.js 中,理解这段逻辑是掌握触发器语义的核心:
pushTriggerItem: (triggerItem) => { if ( isAlreadyProcessed(triggerItem.meta.internalId) && !$.execution.testRun ) { // early exit as we do not want to process duplicate items in actual executions throw new AlreadyProcessedError(); } $.triggerOutput.data.push(triggerItem); if ($.execution.testRun && !isWebhookApp && !isFormsApp) { // early exit after receiving one item as it is enough for test execution throw new EarlyExitError(); } },由此可以提炼出三个关键行为:
- 基于 internalId 去重:引擎会加载该流最近处理过的 internalId 列表(
flow.lastInternalIds(2000),即保留最近 2000 条),当触发器提交的internalId已在列表中且当前不是测试运行(testRun)时,直接抛出AlreadyProcessedError终止本轮,避免同一个日历/事件被重复触发。这正是两个触发器都精心选用etag 作为 internalId的原因——etag 稳定且唯一。 - 测试运行提前终止:在测试模式下(
testRun为 true),收到第一个触发项后立即抛出EarlyExitError结束执行——引擎只需一个样本即可验证触发器是否工作,无需等待分页全部完成。 - 去重窗口有限:由于只保留最近 2000 条 internalId,超过窗口后旧的记录会被淘汰。对于 Google Calendar 这种事件量较小的场景基本无感知,但在高频率创建日历/事件且流长期运行时不处理失败的情况下,理论上存在极低概率的重复触发,设计流程时可考虑配合其他去重手段。
轮询的调度节奏由pollInterval: 15决定——引擎按此间隔周期性调用run($)。这也意味着"新建事件后立即可用"并不成立,实际延迟取决于上一次轮询的时间点,最大约 15 分钟。
七、实战建议与适用范围
结合源码与官方文档,使用这两个触发器时有几点实用建议:
- 选对触发器:监听"用户级"变化(任何日历被创建)用New calendar;监听"某日历内"变化用New event并明确选择目标日历。New event 的
Calendar参数是必填的,建流时记得先完成 Google Calendar 连接,动态数据源才会返回可选项。 - 理解延迟:两者都是 15 分钟轮询,不适合对时效性要求苛刻的场景;若需要秒级响应,应考虑其他实时通道(如直接消费 Google 推送通知,当前仓库未提供对应触发器)。
- 数据下行字段:每个触发项都会把完整的日历/事件资源对象放在
raw中,后续步骤可通过变量面板直接引用如事件标题、开始时间、日历名称等字段,无需额外 HTTP 请求。 - 权限影响范围:授权的
calendar范围是读写级(非只读calendar.readonly),请只在可信环境中使用自己的 OAuth 凭据。
八、总结
Google Calendar 应用的触发器以简洁的官方文档为入口(triggers.md),背后由一套完整的源码体系支撑:newCalendar监听GET /v3/users/me/calendarList的日历列表,newEvent借助listCalendars动态数据源选择日历后监听GET /v3/calendars/{calendarId}/events的事件列表;两者都以 15 分钟为轮询周期、以 etag 为去重标识,并通过defineTrigger校验与引擎的pushTriggerItem机制保证注册合法、执行幂等。掌握这些细节后,你就能在 Automatisch 中快速构建基于 Google Calendar 的自动化流,例如"新事件创建时发送 Slack 通知"或"新日历创建时写入 Google Sheets",并按需评估其 15 分钟延迟对业务场景的适配性。
【免费下载链接】automatischThe open source Zapier alternative. Build workflow automation without spending time and money.项目地址: https://gitcode.com/GitHub_Trending/au/automatisch
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考