Google Chat メッセージのリンクをプレビューする

ユーザーが Google Chat でリンクを共有する際にコンテキストの切り替えが発生しないように、Chat 用アプリでは、メッセージにカードを添付してリンクをプレビューできます。カードには詳細情報が表示され、ユーザーは Google Chat から直接操作できます。

たとえば、会社のすべてのカスタマー サービス エージェントと Case-y という Chat 用アプリを含む Google Chat スペースを考えてみましょう。エージェントは Chat スペースでカスタマー サービス ケースへのリンクを頻繁に共有しますが、同僚はリンクを開いて、割り当て先、ステータス、件名などの詳細を確認する必要があります。同様に、ケースの所有権を取得したり、ステータスを変更したりする場合は、リンクを開く必要があります。

リンクのプレビュー機能を使用すると、スペースの常駐 Chat 用アプリである Case-y は、ケースのリンクが共有されるたびに、割り当て先、ステータス、件名を示すカードを添付できます。カードのボタンを使用すると、エージェントはチャット ストリームから直接ケースの所有権を取得し、ステータスを変更できます。

メッセージにリンクを追加すると、Chat 用アプリがリンクをプレビューする可能性があることを知らせるチップが表示されます。

Chat 用アプリがリンクをプレビューする可能性があることを示すチップ

メッセージを送信すると、リンクが Chat 用アプリに送信され、アプリがカードを生成してユーザーのメッセージに添付します。

メッセージにカードを添付してリンクをプレビューする Chat 用アプリ

リンクの横に、ボタンなどのインタラクティブな要素を含む、リンクに関する追加情報が表示されます。Chat 用アプリは、ボタンクリックなどのユーザー操作に応じて、添付されたカードを更新できます。

メッセージにカードを添付して Chat アプリでリンクをプレビューしたくない場合は、プレビュー チップの をクリックしてプレビューを無効にできます。ユーザーは、[プレビューを削除] をクリックして、添付したカードをいつでも削除できます。

前提条件

HTTP

Google Chat を拡張する Google Workspace アドオン。作成するには、HTTP クイックスタートを完了します。

Apps Script

Google Chat を拡張する Google Workspace アドオン。作成するには、Apps Script のクイックスタートを完了します。

example.comsupport.example.comsupport.example.com/cases/ などの特定のリンクを、Google Cloud コンソールの Chat 用アプリの設定ページで URL パターンとして登録します。これにより、Chat 用アプリでリンクをプレビューできるようになります。

リンクのプレビュー設定メニュー

  1. Google Cloud コンソールを開きます。
  2. [Google Cloud] の横にある下矢印 をクリックして、Chat 用アプリのプロジェクトを開きます。
  3. 検索フィールドに「Google Chat API」と入力し、[Google Chat API] をクリックします。
  4. [Manage] > [Configuration] をクリックします。
  5. [リンク プレビュー] で、URL パターンを追加または編集します。
    1. 新しい URL パターンのリンク プレビューを設定するには、[URL パターンを追加] をクリックします。
    2. 既存の URL パターンの構成を編集するには、下矢印 をクリックします。
  6. [ホスト パターン] フィールドに、URL パターンのドメインを入力します。Chat アプリでこのドメインへのリンクがプレビューされます。

    Chat 用アプリで subdomain.example.com などの特定のサブドメインのリンクをプレビューするには、サブドメインを含めます。

    ドメイン全体のリンクをチャットアプリでプレビューするには、サブドメインとしてアスタリスク(*)を含むワイルドカード文字を指定します。たとえば、*.example.comsubdomain.example.com および any.number.of.subdomains.example.com と一致します。

  7. [パスの接頭辞] フィールドに、ホストパターンのドメインに追加するパスを入力します。

    ホストパターンのドメイン内のすべての URL に一致させるには、[パスの接頭辞] を空白のままにします。

    たとえば、ホストパターンが support.example.com の場合、support.example.com/cases/ でホストされているケースの URL に一致させるには、cases/ と入力します。

  8. [完了] をクリックします。

  9. [保存] をクリックします。

これで、Chat アプリを含む Chat スペースのメッセージに、リンク プレビュー URL パターンに一致するリンクが追加されるたびに、アプリがリンクをプレビューするようになります。

特定のリンクのリンク プレビューを構成すると、Chat 用アプリは、リンクに詳細情報を付加することで、リンクを認識してプレビューできるようになります。

Chat 用アプリを含む Chat スペース内で、ユーザーのメッセージにリンク プレビューの URL パターンと一致するリンクが含まれている場合、Chat 用アプリは MessagePayload を含むイベント オブジェクトを受け取ります。ペイロードの message.matchedUrl オブジェクトには、ユーザーがメッセージに含めたリンクが含まれます。

JSON

message: {
  matchedUrl: {
    url: "https://support.example.com/cases/case123"
  },
  ... // other message attributes redacted
}

Chat 用アプリは、MESSAGE イベント ペイロードに matchedUrl フィールドが存在するかどうかを確認することで、プレビューされたリンクを含むメッセージに情報を追加できます。Chat 用アプリは、基本的なテキスト メッセージで返信するか、カードを添付できます。

テキスト メッセージで返信する

基本的なレスポンスの場合、Chat 用アプリはリンクにテキスト メッセージで返信することで、リンクをプレビューできます。この例では、リンク プレビュー URL パターンに一致するリンク URL を繰り返すメッセージを添付します。

Node.js

/**
 * Google Cloud Function that handles messages that have links whose
 * URLs match URL patterns configured for link previewing.
 *
 *
 * @param {Object} req Request sent from Google Chat space
 * @param {Object} res Response to send back
 */
exports.previewLinks = function previewLinks(req, res) {
  const chatEvent = req.body.chat;

  // Handle MESSAGE events
  if(chatEvent.messagePayload) {
    return res.send(handlePreviewLink(chatEvent.messagePayload.message));
  // Handle button clicks
  } else if(chatEvent.buttonClickedPayload) {
    return res.send(handleCardClick(chatEvent.buttonClickedPayload.message));
  }
};

/**
 * Respond to messages that have links whose URLs match URL patterns configured
 * for link previewing.
 *
 * @param {Object} chatMessage The chat message object from Google Workspace Add On event.
 * @return {Object} Response to send back depending on the matched URL.
 */
function handlePreviewLink(chatMessage) {
  // If the Chat app does not detect a link preview URL pattern, reply
  // with a text message that says so.
  if (!chatMessage.matchedUrl) {
    return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
      text: 'No matchedUrl detected.'
    }}}}};
  }

  // Reply with a text message for URLs of the subdomain "text"
  if (chatMessage.matchedUrl.url.includes("text.example.com")) {
    return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
      text: 'event.chat.messagePayload.message.matchedUrl.url: ' + chatMessage.matchedUrl.url
    }}}}};
  }
}

Apps Script

/**
 * Reply to messages that have links whose URLs match the pattern
 * "text.example.com" configured for link previewing.
 *
 * @param {Object} event The event object from Google Workspace add-on.
 *
 * @return {Object} The action response.
 */
function onMessage(event) {
  // Stores the Google Chat event as a variable.
  const chatMessage = event.chat.messagePayload.message;

  // If the Chat app doesn't detect a link preview URL pattern, reply
  // with a text message that says so.
  if (!chatMessage.matchedUrl) {
    return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
      text: 'No matchedUrl detected.'
    }}}}};
  }

  // Reply with a text message for URLs of the subdomain "text".
  if (chatMessage.matchedUrl.url.includes("text.example.com")) {
    return { hostAppDataAction: { chatDataAction: { createMessageAction: { message: {
      text: 'event.chat.messagePayload.message.matchedUrl.url: ' + chatMessage.matchedUrl.url
    }}}}};
  }
}

プレビューされたリンクにカードを添付するには、タイプ UpdateInlinePreviewActionChatDataActionMarkup オブジェクトを含むアクション DataActions を返します。

次の例では、Chat 用アプリが URL パターン support.example.com を含むメッセージにプレビュー カードを追加します。

メッセージにカードを添付してリンクをプレビューする Chat 用アプリ

Node.js

/**
 * Google Cloud Function that handles messages that have links whose
 * URLs match URL patterns configured for link previewing.
 *
 *
 * @param {Object} req Request sent from Google Chat space
 * @param {Object} res Response to send back
 */
exports.previewLinks = function previewLinks(req, res) {
  const chatEvent = req.body.chat;

  // Handle MESSAGE events
  if(chatEvent.messagePayload) {
    return res.send(handlePreviewLink(chatEvent.messagePayload.message));
  // Handle button clicks
  } else if(chatEvent.buttonClickedPayload) {
    return res.send(handleCardClick(chatEvent.buttonClickedPayload.message));
  }
};

/**
 * Respond to messages that have links whose URLs match URL patterns configured
 * for link previewing.
 *
 * @param {Object} chatMessage The chat message object from Google Workspace Add On event.
 * @return {Object} Response to send back depending on the matched URL.
 */
function handlePreviewLink(chatMessage) {
  // Attach a card to the message for URLs of the subdomain "support"
  if (chatMessage.matchedUrl.url.includes("support.example.com")) {
    // A hard-coded card is used in this example. In a real-life scenario,
    // the case information would be fetched and used to build the card.
    return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: { cardsV2: [{
      cardId: 'attachCard',
      card: {
        header: {
          title: 'Example Customer Service Case',
          subtitle: 'Case basics',
        },
        sections: [{ widgets: [
        { decoratedText: { topLabel: 'Case ID', text: 'case123'}},
        { decoratedText: { topLabel: 'Assignee', text: 'Charlie'}},
        { decoratedText: { topLabel: 'Status', text: 'Open'}},
        { decoratedText: { topLabel: 'Subject', text: 'It won\'t turn on...' }},
        { buttonList: { buttons: [{
          text: 'OPEN CASE',
          onClick: { openLink: {
            url: 'https://support.example.com/orders/case123'
          }},
        }, {
          text: 'RESOLVE CASE',
          onClick: { openLink: {
            url: 'https://support.example.com/orders/case123?resolved=y',
          }},
        }, {
          text: 'ASSIGN TO ME',
          // Use runtime environment variable set with self URL
          onClick: { action: { function: process.env.BASE_URL }}
        }]}}
        ]}]
      }
    }]}}}};
  }
}

Apps Script

この例では、カード JSON を返すことでカード メッセージを送信します。Apps Script カードサービスを使用することもできます。

/**
 * Attach a card to messages that have links whose URLs match the pattern
 * "support.example.com" configured for link previewing.
 *
 * @param {Object} event The event object from Google Workspace add-on.
 *
 * @return {Object} The action response.
 */
function onMessage(event) {
  // Stores the Google Chat event as a variable.
  const chatMessage = event.chat.messagePayload.message;

  // Attach a card to the message for URLs of the subdomain "support".
  if (chatMessage.matchedUrl.url.includes("support.example.com")) {
    // A hard-coded card is used in this example. In a real-life scenario,
    // the case information would be fetched and used to build the card.
    return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: { cardsV2: [{
      cardId: 'attachCard',
      card: {
        header: {
          title: 'Example Customer Service Case',
          subtitle: 'Case summary',
        },
        sections: [{ widgets: [
        { decoratedText: { topLabel: 'Case ID', text: 'case123'}},
        { decoratedText: { topLabel: 'Assignee', text: 'Charlie'}},
        { decoratedText: { topLabel: 'Status', text: 'Open'}},
        { decoratedText: { topLabel: 'Subject', text: 'It won\'t turn on...' }},
        { buttonList: { buttons: [{
          text: 'OPEN CASE',
          onClick: { openLink: {
            url: 'https://support.example.com/orders/case123'
          }},
        }, {
          text: 'RESOLVE CASE',
          onClick: { openLink: {
            url: 'https://support.example.com/orders/case123?resolved=y',
          }},
        }, {
          text: 'ASSIGN TO ME',
          // Clicking this button triggers the execution of the function
          // "assign" from the Apps Script project.
          onClick: { action: { function: 'assign'}}
        }]}}
        ]}]
      }
    }]}}}};
  }
}

ユーザーがリンク プレビュー カードを操作したとき(カードのボタンをクリックしたときなど)に、Chat 用アプリでリンク プレビュー カードを更新できます。

カードを更新するには、Chat 用アプリが次のいずれかの ChatDataActionMarkup オブジェクトを含むアクション DataActions を返す必要があります。

  • ユーザーがメッセージを送信した場合は、UpdateMessageAction オブジェクトを返します。
  • Chat 用アプリがメッセージを送信した場合は、UpdateInlinePreviewAction オブジェクトを返します。

メッセージの送信者を特定するには、イベント ペイロード(buttonClickedPayload)を使用して、送信者(message.sender.type)が HUMAN(ユーザー)または BOT(Chat 用アプリ)に設定されているかどうかを確認します。

次の例は、ユーザーが [自分に割り当てる] ボタンをクリックするたびに、Chat 用アプリがカードの [割り当て先] フィールドを更新してボタンを無効にすることで、リンクのプレビューを更新する方法を示しています。

メッセージに添付されたカードの更新バージョンを含むリンクをプレビューするチャットアプリ

Node.js

/**
 * Google Cloud Function that handles messages that have links whose
 * URLs match URL patterns configured for link previewing.
 *
 *
 * @param {Object} req Request sent from Google Chat space
 * @param {Object} res Response to send back
 */
exports.previewLinks = function previewLinks(req, res) {
  const chatEvent = req.body.chat;

  // Handle MESSAGE events
  if(chatEvent.messagePayload) {
    return res.send(handlePreviewLink(chatEvent.messagePayload.message));
  // Handle button clicks
  } else if(chatEvent.buttonClickedPayload) {
    return res.send(handleCardClick(chatEvent.buttonClickedPayload.message));
  }
};

/**
 * Respond to clicks by assigning user and updating the card that was attached to a
 * message with a previewed link.
 *
 * @param {Object} chatMessage The chat message object from Google Workspace Add On event.
 * @return {Object} Action response depending on the original message.
 */
function handleCardClick(chatMessage) {
  // Creates the updated card that displays "You" for the assignee
  // and that disables the button.
  //
  // A hard-coded card is used in this example. In a real-life scenario,
  // an actual assign action would be performed before building the card.
  const message = { cardsV2: [{
    cardId: 'attachCard',
    card: {
      header: {
        title: 'Example Customer Service Case',
        subtitle: 'Case basics',
      },
      sections: [{ widgets: [
        { decoratedText: { topLabel: 'Case ID', text: 'case123'}},
        // The assignee is now "You"
        { decoratedText: { topLabel: 'Assignee', text: 'You'}},
        { decoratedText: { topLabel: 'Status', text: 'Open'}},
        { decoratedText: { topLabel: 'Subject', text: 'It won\'t turn on...' }},
        { buttonList: { buttons: [{
          text: 'OPEN CASE',
          onClick: { openLink: {
            url: 'https://support.example.com/orders/case123'
          }},
        }, {
          text: 'RESOLVE CASE',
          onClick: { openLink: {
            url: 'https://support.example.com/orders/case123?resolved=y',
          }},
        }, {
          text: 'ASSIGN TO ME',
          // The button is now disabled
          disabled: true,
          // Use runtime environment variable set with self URL
          onClick: { action: { function: process.env.BASE_URL }}
        }]}}
      ]}]
    }
  }]};

  // Checks whether the message event originated from a human or a Chat app
  // to return the adequate action response.
  if(chatMessage.sender.type === 'HUMAN') {
    return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: message }}};
  } else {
    return { hostAppDataAction: { chatDataAction: { updateMessageAction: message }}};
  }
}

Apps Script

この例では、カード JSON を返すことでカード メッセージを送信します。Apps Script カードサービスを使用することもできます。

/**
 * Assigns and updates the card that's attached to a message with a
 * previewed link of the pattern "support.example.com".
 *
 * @param {Object} event The event object from the Google Workspace add-on.
 *
 * @return {Object} Action response depending on the message author.
 */
function assign(event) {
  // Creates the updated card that displays "You" for the assignee
  // and that disables the button.
  //
  // A hard-coded card is used in this example. In a real-life scenario,
  // an actual assign action would be performed before building the card.
  const message = { cardsV2: [{
    cardId: 'attachCard',
    card: {
      header: {
        title: 'Example Customer Service Case',
        subtitle: 'Case summary',
      },
      sections: [{ widgets: [
        { decoratedText: { topLabel: 'Case ID', text: 'case123'}},
        // The assignee is now "You"
        { decoratedText: { topLabel: 'Assignee', text: 'You'}},
        { decoratedText: { topLabel: 'Status', text: 'Open'}},
        { decoratedText: { topLabel: 'Subject', text: 'It won\'t turn on...' }},
        { buttonList: { buttons: [{
          text: 'OPEN CASE',
          onClick: { openLink: {
            url: 'https://support.example.com/orders/case123'
          }},
        }, {
          text: 'RESOLVE CASE',
          onClick: { openLink: {
            url: 'https://support.example.com/orders/case123?resolved=y',
          }},
        }, {
          text: 'ASSIGN TO ME',
          // The button is now disabled
          disabled: true,
          onClick: { action: { function: 'assign'}}
        }]}}
      ]}]
    }
  }]};

  // Use the adequate action response type. It depends on whether the message
  // the preview link card is attached to was created by a human or a Chat app.
  if(event.chat.buttonClickedPayload.message.sender.type === 'HUMAN') {
    return { hostAppDataAction: { chatDataAction: { updateInlinePreviewAction: message }}};
  } else {
    return { hostAppDataAction: { chatDataAction: { updateMessageAction: message }}};
  }
}

制限事項と考慮事項

Chat 用アプリのリンク プレビューを構成する際は、次の制限事項と考慮事項に注意してください。

  • 各 Chat 用アプリは、最大 5 つの URL パターンのリンクのプレビューをサポートします。
  • Chat アプリでは、メッセージごとに 1 つのリンクがプレビューされます。1 つのメッセージに複数のプレビュー可能なリンクが含まれている場合、最初のプレビュー可能なリンクのみがプレビューされます。
  • チャットアプリは https:// で始まるリンクのみをプレビューするため、https://support.example.com/cases/ はプレビューされますが、support.example.com/cases/ はプレビューされません。
  • メッセージに スラッシュ コマンドなど、Chat 用アプリに送信される他の情報が含まれていない限り、リンク プレビューによって Chat 用アプリに送信されるのはリンクの URL のみです。
  • ユーザーがリンクを投稿した場合、Chat 用アプリがリンクのプレビュー カードを更新できるのは、ユーザーがボタンのクリックなど、カードを操作した場合のみです。Chat API の Message リソースで update() メソッドを呼び出して、ユーザーのメッセージを非同期で更新することはできません。
  • Chat 用アプリはスペース内のすべてのユーザーに対してリンクをプレビューする必要があるため、メッセージで privateMessageViewer フィールドを省略する必要があります。

リンク プレビューを実装する際に、アプリのログを読んで Chat 用アプリをデバッグする必要がある場合があります。ログを読み取るには、Google Cloud コンソールの [ログ エクスプローラ] にアクセスします。