制定会议议程

编码级别:初级
时长:15 分钟(
项目类型:采用事件驱动型触发器的自动化

目标

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

关于此解决方案

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

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

工作原理

脚本将创建一个日程文档模板。更新 脚本会检查您所拥有的活动是否包含“#agenda”在 。如果存在该标记,脚本会复制该模板, 将其添加到日历活动并与活动参加者共享。

Apps 脚本服务

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

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

前提条件

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

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

设置脚本

  1. 点击下面的按钮,打开为会议制定日程示例 Apps 脚本项目。
    打开项目
  2. 点击概览
  3. 在概览页面上,点击“复制”图标 用于制作副本的图标
  4. 在复制的项目中,从函数下拉菜单中选择 setUp
  5. 点击运行
  6. 出现提示时,为脚本授权。 如果 OAuth 同意屏幕显示以下警告:“此应用未经验证”, 选择高级 > 以继续 前往“{Project Name}”(不安全)

运行脚本

  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. 打开 Agenda TEMPLATE## 文档并进行修改。

贡献者

此样本由产品管理团队和平台团队的 Jeremy Glassenberg 创建 战略顾问。您可以在 Twitter 上关注 Jeremy @jglassenberg

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

后续步骤