制定会议议程

编码级别:新手
时长:15 分钟
项目类型:使用事件驱动型触发器实现自动化

目标

  • 了解解决方案的用途。
  • 了解 Apps 脚本服务在此解决方案中执行的操作。
  • 设置脚本。
  • 运行脚本。

关于此解决方案

在 Google 文档中自动创建日程文档,并将其附加到您的 Google 日历会议。

添加到日历活动的日程的屏幕截图

运作方式

脚本为日程创建文档模板。当您更新日历时,该脚本会检查您拥有的任何活动的说明中是否有“#agenda”。如果存在此标记,该脚本将复制模板,将其添加到日历活动,并与活动参加者共享。

Apps 脚本服务

此解决方案使用以下服务:

  • 云端硬盘服务 - 检查模板文档是否存在;如果不存在,则为模板文档创建一个新文件夹。 为每个新日程创建模板文档的副本。
  • 文档服务 - 创建日程模板。
  • 日历服务 - 检查带有“#agenda”标记的活动,并使用指向日程文档的链接更新活动说明。
  • 基础服务 - 使用 Session 类获取用户的电子邮件地址。这有助于为当前用户构建触发器。
  • 脚本服务 - 创建在每次用户日历发生更改时触发的触发器。

前提条件

如需使用此示例,您需要满足以下前提条件:

  • Google 帐号(Google Workspace 帐号可能需要管理员批准)。
  • 能够访问互联网的网络浏览器。

设置脚本

  1. 点击下面的按钮可打开示例为会议制定日程。Apps 脚本项目。
    打开项目
  2. 点击概览图标
  3. 在概览页面上,点击“复制”图标 用于制作副本的图标
  4. 在复制的项目中,从函数下拉菜单中选择 setUp
  5. 点击运行
  6. 出现提示时,为脚本授权。 如果 OAuth 同意屏幕显示 This app is not verify 警告,请依次选择 Advanced > Go to {Project Name} (unsafe),以继续操作。

运行脚本

  1. 打开 Google 日历
  2. 创建新活动或修改现有活动。
  3. 在说明中,添加 #agenda 并保存活动。
  4. 请查看您的电子邮件,获取有文档与您共享的电子邮件通知,或者刷新日历并再次点击活动,以查看指向日程文档的链接。

所有参加者都会收到查看日程的电子邮件通知。该脚本会向参加者授予编辑权限,但您可以编辑此脚本以更新参加者的日程文档权限

查看代码

要查看此解决方案的 Apps 脚本代码,请点击下面的查看源代码

查看源代码

Code.gs

solutions/automations/agenda-maker/Code.js
// To learn how to use this script, refer to the documentation:
// https://developers.google.com/apps-script/samples/automations/agenda-maker

/*
Copyright 2022 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/**
 * Checks if the folder for Agenda docs exists, and creates it if it doesn't.
 *
 * @return {*} Drive folder ID for the app.
 */
function checkFolder() {
  const folders = DriveApp.getFoldersByName('Agenda Maker - App');
  // Finds the folder if it exists
  while (folders.hasNext()) {
    let folder = folders.next();
    if (
      folder.getDescription() ==
        'Apps Script App - Do not change this description' &&
      folder.getOwner().getEmail() == Session.getActiveUser().getEmail()
    ) {
      return folder.getId();
    }
  }
  // If the folder doesn't exist, creates one
  let folder = DriveApp.createFolder('Agenda Maker - App');
  folder.setDescription('Apps Script App - Do not change this description');
  return folder.getId();
}

/**
 * Finds the template agenda doc, or creates one if it doesn't exist.
 */
function getTemplateId(folderId) {
  const folder = DriveApp.getFolderById(folderId);
  const files = folder.getFilesByName('Agenda TEMPLATE##');

  // If there is a file, returns the ID.
  while (files.hasNext()) {
    const file = files.next();
    return file.getId();
  }

  // Otherwise, creates the agenda template.
  // You can adjust the default template here
  const doc = DocumentApp.create('Agenda TEMPLATE##');
  const body = doc.getBody();

  body
      .appendParagraph('##Attendees##')
      .setHeading(DocumentApp.ParagraphHeading.HEADING1)
      .editAsText()
      .setBold(true);
  body.appendParagraph(' ').editAsText().setBold(false);

  body
      .appendParagraph('Overview')
      .setHeading(DocumentApp.ParagraphHeading.HEADING1)
      .editAsText()
      .setBold(true);
  body.appendParagraph(' ');
  body.appendParagraph('- Topic 1: ').editAsText().setBold(true);
  body.appendParagraph(' ').editAsText().setBold(false);
  body.appendParagraph('- Topic 2: ').editAsText().setBold(true);
  body.appendParagraph(' ').editAsText().setBold(false);
  body.appendParagraph('- Topic 3: ').editAsText().setBold(true);
  body.appendParagraph(' ').editAsText().setBold(false);

  body
      .appendParagraph('Next Steps')
      .setHeading(DocumentApp.ParagraphHeading.HEADING1)
      .editAsText()
      .setBold(true);
  body.appendParagraph('- Takeaway 1: ').editAsText().setBold(true);
  body.appendParagraph('- Responsible: ').editAsText().setBold(false);
  body.appendParagraph('- Accountable: ');
  body.appendParagraph('- Consult: ');
  body.appendParagraph('- Inform: ');
  body.appendParagraph(' ');
  body.appendParagraph('- Takeaway 2: ').editAsText().setBold(true);
  body.appendParagraph('- Responsible: ').editAsText().setBold(false);
  body.appendParagraph('- Accountable: ');
  body.appendParagraph('- Consult: ');
  body.appendParagraph('- Inform: ');
  body.appendParagraph(' ');
  body.appendParagraph('- Takeaway 3: ').editAsText().setBold(true);
  body.appendParagraph('- Responsible: ').editAsText().setBold(false);
  body.appendParagraph('- Accountable: ');
  body.appendParagraph('- Consult: ');
  body.appendParagraph('- Inform: ');

  doc.saveAndClose();

  folder.addFile(DriveApp.getFileById(doc.getId()));

  return doc.getId();
}

/**
 * When there is a change to the calendar, searches for events that include "#agenda"
 * in the decrisption.
 *
 */
function onCalendarChange() {
  // Gets recent events with the #agenda tag
  const now = new Date();
  const events = CalendarApp.getEvents(
      now,
      new Date(now.getTime() + 2 * 60 * 60 * 1000000),
      {search: '#agenda'},
  );

  const folderId = checkFolder();
  const templateId = getTemplateId(folderId);

  const folder = DriveApp.getFolderById(folderId);

  // Loops through any events found
  for (i = 0; i < events.length; i++) {
    const event = events[i];

    // Confirms whether the event has the #agenda tag
    let description = event.getDescription();
    if (description.search('#agenda') == -1) continue;

    // Only works with events created by the owner of this calendar
    if (event.isOwnedByMe()) {
      // Creates a new document from the template for an agenda for this event
      const newDoc = DriveApp.getFileById(templateId).makeCopy();
      newDoc.setName('Agenda for ' + event.getTitle());

      const file = DriveApp.getFileById(newDoc.getId());
      folder.addFile(file);

      const doc = DocumentApp.openById(newDoc.getId());
      const body = doc.getBody();

      // Fills in the template with information about the attendees from the
      // calendar event
      const conf = body.findText('##Attendees##');
      if (conf) {
        const ref = conf.getStartOffset();

        for (let i in event.getGuestList()) {
          let guest = event.getGuestList()[i];

          body.insertParagraph(ref + 2, guest.getEmail());
        }
        body.replaceText('##Attendees##', 'Attendees');
      }

      // Replaces the tag with a link to the agenda document
      const agendaUrl = 'https://docs.google.com/document/d/' + newDoc.getId();
      description = description.replace(
          '#agenda',
          '<a href=' + agendaUrl + '>Agenda Doc</a>',
      );
      event.setDescription(description);

      // Invites attendees to the Google doc so they automatically receive access to the agenda
      newDoc.addEditor(newDoc.getOwner());

      for (let i in event.getGuestList()) {
        let guest = event.getGuestList()[i];

        newDoc.addEditor(guest.getEmail());
      }
    }
  }
  return;
}

/**
 * Creates an event-driven trigger that fires whenever there's a change to the calendar.
 */
function setUp() {
  let email = Session.getActiveUser().getEmail();
  ScriptApp.newTrigger("onCalendarChange").forUserCalendar(email).onEventUpdated().create();
}

修改

您可以根据需要随意修改示例。下面是您可以进行的一些可选更改。

更新参加者的日程文档权限

此脚本向参加者授予了编辑权限。如果您希望将权限限制为只能查看,请在代码的以下部分中将 addEditor 方法替换为 addViewer 方法:

     for (let i in event.getGuestList()) {
       let guest = event.getGuestList()[i];

       newDoc.addEditor(guest.getEmail());

修改日程文档模板

如要更新日程文档模板,请按以下步骤操作:

  1. 在日历活动中创建第一个日程后,请打开 Google 云端硬盘。
  2. 打开名为 Agenda Maker - App 的文件夹。
  3. 打开日程模板##文档并进行修改。

贡献者

此示例由产品管理和平台策略顾问 Jeremy Glassenberg 创建。您可以在 Twitter (@jglassenberg) 上关注 Jeremy。

此示例由 Google 在 Google 开发者专家的帮助下进行维护。

后续步骤