使用 Compose 操作扩展 Compose 界面

除了在用户阅读 Gmail 邮件时提供基于卡片的界面之外,扩展 Gmail 的 Google Workspace 加购项还可以在用户撰写新邮件或回复现有邮件时提供另一个界面。这样一来,插件就可以自动执行为用户撰写电子邮件的任务。

访问 Google Workspace 插件撰写界面

您可以通过两种方式查看插件的撰写界面。第一种方法是在插件已打开的情况下开始撰写新草稿或回复。第二种方法是在撰写草稿时启动该插件。

无论哪种情况,都会导致插件执行插件清单中定义的相应 Compose 触发器函数。撰写触发函数会为相应撰写操作构建撰写界面,然后 Gmail 会向用户显示该界面。

构建撰写插件

您可以按照以下常规步骤向插件添加撰写功能:

  1. gmail.composeTrigger 字段添加到插件脚本项目的清单,并更新清单范围以包含撰写操作所需的范围。
  2. 实现一个撰写触发器函数,该函数会在触发器触发时构建撰写界面。Compose 触发函数会返回单个 Card 对象或 Card 对象数组,这些对象共同构成相应 Compose 操作的 Compose 界面。
  3. 实现需要用来响应用户 Compose 界面互动的关联回调函数。这些函数不是撰写操作本身(该操作只会使撰写界面显示出来);相反,这些函数是用于控制在选择撰写界面的不同元素时会发生什么情况的各个函数。例如,包含按钮的界面卡片通常具有关联的回调函数,该函数会在用户点击该按钮时执行。用于更新草稿消息内容的小部件的回调函数应返回 UpdateDraftActionResponse 对象。

撰写触发函数

插件的 Compose 界面与插件的消息界面采用相同的构建方式,即使用 Apps 脚本的 Card 服务来构建卡片并使用微件填充卡片。

您必须实现清单中定义的 gmail.composeTrigger.selectActions[].runFunction。Compose 触发函数必须返回单个 Card 对象或一个 Card 对象数组,以构成相应操作的 Compose 界面。这些函数与上下文触发函数非常相似,应以相同的方式构建卡片。

组合触发事件对象

当用户选择撰写操作时,系统会执行相应的撰写触发函数,并将事件对象作为参数传递给该函数。事件对象可以将有关插件上下文和正在撰写的草稿的信息传递给触发函数。

如需详细了解信息在事件对象中的排列方式,请参阅事件对象结构。事件对象中包含的信息部分受 gmail.composeTrigger.draftAccess 清单字段的值控制:

  • 如果 gmail.composeTrigger.draftAccess 清单字段为 NONE 或未包含,则事件对象仅包含最少的信息。

  • 如果 gmail.composeTrigger.draftAccess 设置为 METADATA,则传递给撰写触发器函数的事件对象会填充正在撰写的电子邮件的收件人列表。使用 METADATA 草稿访问权限要求插件清单包含 https://www.googleapis.com/auth/gmail.addons.current.message.metadata Gmail 范围

将内容插入到有效草稿中

通常,附加组件可组合界面会提供用户选项和控件,帮助用户撰写消息。对于这些使用情形,用户在界面中做出选择后,插件会解读这些选择,并相应地更新当前正在处理的电子邮件草稿。

为了更轻松地更新当前草稿电子邮件,卡片服务已扩展为包含以下类:

通常,插件 Compose 界面会包含“保存”或“插入”微件,用户可以点击这些微件来表明他们已完成在界面中的选择,并希望将所选内容添加到正在撰写的电子邮件中。若要添加这种互动性,微件应具有关联的 Action 对象,该对象指示插件在点击微件时运行特定的回调函数。您必须实现这些回调函数。每个回调函数都应返回一个内置的 UpdateDraftActionResponse 对象,其中详细说明了要对当前草稿电子邮件进行的更改。

示例 1

以下代码段展示了如何构建一个 Compose 界面,用于更新当前电子邮件草稿的主题以及“收件人”“抄送”和“密送”收件人。

/**
 * Compose trigger function that fires when the compose UI is
 * requested. Builds and returns a compose UI for inserting images.
 *
 * @param {event} e The compose trigger event object. Not used in
 *         this example.
 * @return {Card[]}
 */
function getComposeUI(e) {
  return [buildComposeCard()];
}

/**
 * Build a card to display interactive buttons to allow the user to
 * update the subject, and To, Cc, Bcc recipients.
 *
 * @return {Card}
 */
function buildComposeCard() {

  var card = CardService.newCardBuilder();
  var cardSection = CardService.newCardSection().setHeader('Update email');
  cardSection.addWidget(
      CardService.newTextButton()
          .setText('Update subject')
          .setOnClickAction(CardService.newAction()
              .setFunctionName('applyUpdateSubjectAction')));
  cardSection.addWidget(
      CardService.newTextButton()
          .setText('Update To recipients')
          .setOnClickAction(CardService.newAction()
              .setFunctionName('updateToRecipients')));
  cardSection.addWidget(
      CardService.newTextButton()
          .setText('Update Cc recipients')
          .setOnClickAction(CardService.newAction()
              .setFunctionName('updateCcRecipients')));
  cardSection.addWidget(
      CardService.newTextButton()
          .setText('Update Bcc recipients')
          .setOnClickAction(CardService.newAction()
              .setFunctionName('updateBccRecipients')));
  return card.addSection(cardSection).build();
}

/**
 * Updates the subject field of the current email when the user clicks
 * on "Update subject" in the compose UI.
 *
 * Note: This is not the compose action that builds a compose UI, but
 * rather an action taken when the user interacts with the compose UI.
 *
 * @return {UpdateDraftActionResponse}
 */
function applyUpdateSubjectAction() {
  // Get the new subject field of the email.
  // This function is not shown in this example.
  var subject = getSubject();
  var response = CardService.newUpdateDraftActionResponseBuilder()
      .setUpdateDraftSubjectAction(CardService.newUpdateDraftSubjectAction()
          .addUpdateSubject(subject))
      .build();
  return response;
}

/**
 * Updates the To recipients of the current email when the user clicks
 * on "Update To recipients" in the compose UI.
 *
 * Note: This is not the compose action that builds a compose UI, but
 * rather an action taken when the user interacts with the compose UI.
 *
 * @return {UpdateDraftActionResponse}
 */
function applyUpdateToRecipientsAction() {
  // Get the new To recipients of the email.
  // This function is not shown in this example.
  var toRecipients = getToRecipients();
  var response = CardService.newUpdateDraftActionResponseBuilder()
      .setUpdateDraftToRecipientsAction(CardService.newUpdateDraftToRecipientsAction()
          .addUpdateToRecipients(toRecipients))
      .build();
  return response;
}

/**
 * Updates the Cc recipients  of the current email when the user clicks
 * on "Update Cc recipients" in the compose UI.
 *
 * Note: This is not the compose action that builds a compose UI, but
 * rather an action taken when the user interacts with the compose UI.
 *
 * @return {UpdateDraftActionResponse}
 */
function applyUpdateCcRecipientsAction() {
  // Get the new Cc recipients of the email.
  // This function is not shown in this example.
  var ccRecipients = getCcRecipients();
  var response = CardService.newUpdateDraftActionResponseBuilder()
      .setUpdateDraftCcRecipientsAction(CardService.newUpdateDraftCcRecipientsAction()
          .addUpdateToRecipients(ccRecipients))
      .build();
  return response;
}

/**
 * Updates the Bcc recipients  of the current email when the user clicks
 * on "Update Bcc recipients" in the compose UI.
 *
 * Note: This is not the compose action that builds a compose UI, but
 * rather an action taken when the user interacts with the compose UI.
 *
 * @return {UpdateDraftActionResponse}
 */
function applyUpdateBccRecipientsAction() {
  // Get the new Bcc recipients of the email.
  // This function is not shown in this example.
  var bccRecipients = getBccRecipients();
  var response = CardService.newUpdateDraftActionResponseBuilder()
      .setUpdateDraftBccRecipientsAction(CardService.newUpdateDraftBccRecipientsAction()
          .addUpdateToRecipients(bccRecipients))
      .build();
  return response;
}

示例 2

以下代码段展示了如何构建一个将图片插入当前电子邮件草稿的 Compose 界面。

/**
 * Compose trigger function that fires when the compose UI is
 * requested. Builds and returns a compose UI for inserting images.
 *
 * @param {event} e The compose trigger event object. Not used in
 *         this example.
 * @return {Card[]}
 */
function getInsertImageComposeUI(e) {
  return [buildImageComposeCard()];
}

/**
 * Build a card to display images from a third-party source.
 *
 * @return {Card}
 */
function buildImageComposeCard() {
  // Get a short list of image URLs to display in the UI.
  // This function is not shown in this example.
  var imageUrls = getImageUrls();

  var card = CardService.newCardBuilder();
  var cardSection = CardService.newCardSection().setHeader('My Images');
  for (var i = 0; i < imageUrls.length; i++) {
    var imageUrl = imageUrls[i];
    cardSection.addWidget(
        CardService.newImage()
            .setImageUrl(imageUrl)
            .setOnClickAction(CardService.newAction()
                  .setFunctionName('applyInsertImageAction')
                  .setParameters({'url' : imageUrl})));
  }
  return card.addSection(cardSection).build();
}

/**
 * Adds an image to the current draft email when the image is clicked
 * in the compose UI. The image is inserted at the current cursor
 * location. If any content of the email draft is currently selected,
 * it is deleted and replaced with the image.
 *
 * Note: This is not the compose action that builds a compose UI, but
 * rather an action taken when the user interacts with the compose UI.
 *
 * @param {event} e The incoming event object.
 * @return {UpdateDraftActionResponse}
 */
function applyInsertImageAction(e) {
  var imageUrl = e.parameters.url;
  var imageHtmlContent = '<img style=\"display: block\" src=\"'
        + imageUrl + '\"/>';
  var response = CardService.newUpdateDraftActionResponseBuilder()
      .setUpdateDraftBodyAction(CardService.newUpdateDraftBodyAction()
          .addUpdateContent(
              imageHtmlContent,
              CardService.ContentType.MUTABLE_HTML)
          .setUpdateType(
              CardService.UpdateDraftBodyType.IN_PLACE_INSERT))
      .build();
  return response;
}