ユーザーが Google Chat でリンクを共有する際にコンテキストの切り替えが行われないように、Chat アプリではメッセージにカードを添付してリンクをプレビューできます。このカードには詳細情報が表示され、ユーザーは Google Chat から直接操作を行うことができます。
たとえば、Google Chat スペースに会社のすべてのカスタマー サービス エージェントと Case-y という名前の Chat アプリがあるとします。エージェントは Chat スペースでカスタマー サービス ケースへのリンクを頻繁に共有します。同僚が作業を行うたびに、ケースリンクを開いて割り当て先、ステータス、件名などの詳細を確認する必要があります。同様に、ユーザーがケースのオーナー権限の取得やステータスの変更を希望する場合は、リンクを開く必要があります。
リンク プレビューを使用すると、スペースの常駐 Chat アプリである Case-y は、誰かがケースリンクを共有するたびに、担当者、ステータス、件名を示すカードを添付できます。エージェントはカードのボタンを使って、ケースを担当したり、チャット ストリームからステータスを直接変更したりできます。
リンクのプレビューの仕組み
ユーザーがメッセージにリンクを追加すると、Chat アプリでリンクがプレビューされる可能性があることを知らせるチップが表示されます。

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

リンクの横には、ボタンなどのインタラクティブな要素など、リンクに関する追加情報が表示されます。Chat アプリでは、ボタンのクリックなどのユーザー操作に応じて、添付のカードを更新できます。
メッセージにカードを追加してリンクをプレビューしたくないユーザーは、プレビュー チップの cancel をクリックしてプレビューできないようにします。ユーザーは [プレビューを削除] をクリックすることで、添付されたカードをいつでも削除できます。
Google Cloud コンソールの Chat アプリの構成ページで example.com
、support.example.com
、support.example.com/cases/
などの特定のリンクを URL パターンとして登録し、Chat アプリでプレビューできるようにします。

- Google Cloud コンソールを開きます。
- [Google Cloud] の横にある下矢印 arrow_drop_down をクリックし、Chat アプリのプロジェクトを開きます。
- 検索フィールドに「
Google Chat API
」と入力して [Google Chat API] をクリックします。
- [Manage] > [Configuration] をクリックします。
- [リンクのプレビュー] で、URL パターンを追加または編集します。
- 新しい URL パターンのリンク プレビューを設定するには、[URL パターンを追加] をクリックします。
- 既存の URL パターンの設定を編集するには、下矢印 expand_more をクリックします。
[ホストパターン] 欄に、URL パターンのドメインを入力します。このドメインへのリンクがプレビューされます。
特定のサブドメイン(subdomain.example.com
など)の Chat アプリにプレビュー リンクを設定するには、サブドメインを含めます。
Chat アプリでドメイン全体のリンクをプレビューするには、ワイルドカード文字とアスタリスク(*)をサブドメインとして指定します。たとえば、*.example.com
は subdomain.example.com
および any.number.of.subdomains.example.com
と一致します。
[パス プレフィックス] フィールドに、ホストパターン ドメインに追加するパスを入力します。
ホストパターン ドメイン内のすべての URL に一致させるには、[パスの接頭辞] を空のままにします。
たとえば、ホストパターンが support.example.com
の場合、support.example.com/cases/
でホストされているケースの URL を照合するには、「cases/
」と入力します。
[完了] をクリックします。
[保存] をクリックします。
これで、リンク プレビュー URL パターンと一致するリンクが、ユーザーの Chat アプリを含む Chat スペース内のメッセージに含まれる場合、リンクがプレビューされるようになりました。
リンクのプレビュー
特定のリンクのリンク プレビューを構成すると、Chat アプリでリンクを認識し、詳細情報を添付してプレビューできるようになります。
Chat アプリを含む Chat スペース内で、ユーザーのメッセージにリンク プレビュー URL パターンに一致するリンクが含まれている場合、Chat アプリは MESSAGE
インタラクション イベントを受け取ります。インタラクション イベントの JSON ペイロードには、matchedUrl
フィールドが含まれています。
JSON
message {
. . . // other message attributes redacted
"matchedUrl": {
"url": "https://support.example.com/cases/case123"
},
. . . // other message attributes redacted
}
MESSAGE
イベント ペイロードに matchedUrl
フィールドが存在するかどうかを確認することで、Chat アプリはプレビュー リンクを含むメッセージに情報を追加できます。Chat アプリでは、シンプルなテキスト メッセージで返信することも、カードを添付することもできます。
テキスト メッセージで返信する
シンプルなレスポンスの場合、チャットアプリはリンクに対してシンプルなテキスト メッセージで返信することで、リンクをプレビューできます。この例では、リンク プレビューの URL パターンに一致するリンク URL を繰り返すメッセージを添付します。
カードを添付
プレビュー済みリンクにカードを添付するには、UPDATE_USER_MESSAGE_CARDS
タイプの ActionResponse
を返します。この例では、シンプルなカードを追加します。

Node.js
/**
* Responds to messages that have links whose URLs match URL patterns
* configured for link previewing.
*
* @param {Object} req Request sent from Google Chat.
* @param {Object} res Response to send back.
*/
exports.onMessage = (req, res) => {
if (req.method === 'GET' || !req.body.message) {
res.send(
'Hello! This function is meant to be used in a Google Chat Space.');
}
// Checks for the presence of event.message.matchedUrl and attaches a card
// if present
if (req.body.message.matchedUrl) {
res.json({
'actionResponse': {'type': 'UPDATE_USER_MESSAGE_CARDS'},
'cardsV2': [
{
'cardId': 'attachCard',
'card': {
'header': {
'title': 'Example Customer Service Case',
'subtitle': 'Case basics',
},
'sections': [
{
'widgets': [
{'keyValue': {'topLabel': 'Case ID', 'content': 'case123'}},
{'keyValue': {'topLabel': 'Assignee', 'content': 'Charlie'}},
{'keyValue': {'topLabel': 'Status', 'content': 'Open'}},
{
'keyValue': {
'topLabel': 'Subject', 'content': 'It won"t turn on...',
}
},
],
},
{
'widgets': [
{
'buttons': [
{
'textButton': {
'text': 'OPEN CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123',
},
},
},
},
{
'textButton': {
'text': 'RESOLVE CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123?resolved=y',
},
},
},
},
{
'textButton': {
'text': 'ASSIGN TO ME',
'onClick': {
'action': {
'actionMethodName': 'assign',
},
},
},
},
],
},
],
},
],
},
},
],
});
}
// If the Chat app doesn’t detect a link preview URL pattern, it says so.
res.json({'text': 'No matchedUrl detected.'});
};
Apps Script
/**
* Responds to messages that have links whose URLs match URL patterns
* configured for link previewing.
*
* @param {Object} event The event object from Chat API.
* @return {Object} Response from the Chat app attached to the message with
* the previewed link.
*/
function onMessage(event) {
// Checks for the presence of event.message.matchedUrl and attaches a card
// if present
if (event.message.matchedUrl) {
return {
'actionResponse': {
'type': 'UPDATE_USER_MESSAGE_CARDS',
},
'cardsV2': [{
'cardId': 'attachCard',
'card': {
'header': {
'title': 'Example Customer Service Case',
'subtitle': 'Case basics',
},
'sections': [{
'widgets': [
{'keyValue': {'topLabel': 'Case ID', 'content': 'case123'}},
{'keyValue': {'topLabel': 'Assignee', 'content': 'Charlie'}},
{'keyValue': {'topLabel': 'Status', 'content': 'Open'}},
{
'keyValue': {
'topLabel': 'Subject', 'content': 'It won\'t turn on...',
},
},
],
},
{
'widgets': [{
'buttons': [
{
'textButton': {
'text': 'OPEN CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123',
},
},
},
},
{
'textButton': {
'text': 'RESOLVE CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123?resolved=y',
},
},
},
},
{
'textButton': {
'text': 'ASSIGN TO ME',
'onClick': {'action': {'actionMethodName': 'assign'}},
},
},
],
}],
}],
},
}],
};
}
// If the Chat app doesn’t detect a link preview URL pattern, it says so.
return {'text': 'No matchedUrl detected.'};
}
カードを更新する
プレビュー済みリンクにアタッチされているカードを更新するには、UPDATE_USER_MESSAGE_CARDS
タイプの ActionResponse
を返します。チャットアプリは、チャットアプリ インタラクション イベントに対するレスポンスとしてリンクをプレビューするカードのみを更新できます。Chat アプリは、Chat API を非同期で呼び出してこれらのカードを更新できません。
リンク プレビューでは、UPDATE_MESSAGE
タイプの ActionResponse
を返すことはできません。UPDATE_MESSAGE
によってカードだけでなくメッセージ全体が更新されるため、チャットアプリが元のメッセージを作成している場合にのみ機能します。リンク プレビューでは、ユーザーが作成したメッセージにカードが添付されるため、Chat アプリにカードを更新する権限はありません。
Chat ストリーム内のユーザーが作成したカードとアプリによって作成されたカードの両方を関数が更新できるようにするには、メッセージの作成者が Chat アプリかユーザーかに基づいて ActionResponse
を動的に設定します。
これを行うには、添付されたカードの onclick
プロパティの一部としてカスタムの actionMethodName
を指定して確認する(メッセージがユーザー作成であることを識別する)と、メッセージの作成者がユーザーであるかどうかを確認するという 2 つの方法があります。
actionMethodName
を使用して、プレビューされたカードの CARD_CLICKED
インタラクション イベントを適切に処理するには、添付するカードの onclick
プロパティの一部としてカスタム actionMethodName
を設定します。
JSON
. . . // Preview card details
{
"textButton": {
"text": "ASSIGN TO ME",
"onClick": {
// actionMethodName identifies the button to help determine the
// appropriate ActionResponse.
"action": {
"actionMethodName": "assign",
}
}
}
}
. . . // Preview card details
リンク プレビューの一部としてボタンを識別する "actionMethodName": "assign"
により、一致する actionMethodName
を確認することで、正しい ActionResponse
を動的に返すことができます。
方法 2: 送信者の種類を確認する
message.sender.type
が HUMAN
か BOT
かを確認します。HUMAN
の場合、ActionResponse
を UPDATE_USER_MESSAGE_CARDS
に設定します。それ以外の場合は、ActionResponse
を UPDATE_MESSAGE
に設定します。方法は次のとおりです。
カードを更新する一般的な理由は、ボタンのクリックがあった場合です。前のセクション「カードを添付」で [自分に割り当てる] ボタンを思い出してください。次の完全な例では、ユーザーが [Assign to Me] をクリックした後に、カードが「あなた」に割り当てられるようにカードを更新します。この例では、送信者タイプを確認して ActionResponse
を動的に設定しています。
完全な例: カスタマー サービスのチャットアプリ Case-y
これは Case-y のコードの全体です。チャットアプリは、カスタマー サービス エージェントが共同作業する Chat スペースで共有されているケースへのリンクをプレビューします。
Node.js
/**
* Responds to messages that have links whose URLs match URL patterns
* configured for link previewing.
*
* @param {Object} req Request sent from Google Chat.
* @param {Object} res Response to send back.
*/
exports.onMessage = (req, res) => {
if (req.method === 'GET' || !req.body.message) {
res.send(
'Hello! This function is meant to be used in a Google Chat Space.');
}
// Checks for the presence of event.message.matchedUrl and attaches a card
// if present
if (req.body.message.matchedUrl) {
res.json(createMessage());
}
// Respond to button clicks on attached cards
if (req.body.type === 'CARD_CLICKED') {
// Checks whether the message event originated from a human or a Chat app
// and sets actionResponse.type to "UPDATE_USER_MESSAGE_CARDS if human or
// "UPDATE_MESSAGE" if Chat app.
const actionResponseType = req.body.action.actionMethodName === 'HUMAN' ?
'UPDATE_USER_MESSAGE_CARDS' :
'UPDATE_MESSAGE';
if (req.body.action.actionMethodName === 'assign') {
res.json(createMessage(actionResponseType, 'You'));
}
}
// If the Chat app doesn’t detect a link preview URL pattern, it says so.
res.json({'text': 'No matchedUrl detected.'});
};
/**
* Message to create a card with the correct response type and assignee.
*
* @param {string} actionResponseType
* @param {string} assignee
* @return {Object} a card with URL preview
*/
function createMessage(
actionResponseType = 'UPDATE_USER_MESSAGE_CARDS',
assignee = 'Charlie'
) {
return {
'actionResponse': {'type': actionResponseType},
'cardsV2': [
{
'cardId': 'previewLink',
'card': {
'header': {
'title': 'Example Customer Service Case',
'subtitle': 'Case basics',
},
'sections': [
{
'widgets': [
{'keyValue': {'topLabel': 'Case ID', 'content': 'case123'}},
{'keyValue': {'topLabel': 'Assignee', 'content': assignee}},
{'keyValue': {'topLabel': 'Status', 'content': 'Open'}},
{
'keyValue': {
'topLabel': 'Subject', 'content': 'It won"t turn on...',
},
},
],
},
{
'widgets': [
{
'buttons': [
{
'textButton': {
'text': 'OPEN CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123',
},
},
},
},
{
'textButton': {
'text': 'RESOLVE CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123?resolved=y',
},
},
},
},
{
'textButton': {
'text': 'ASSIGN TO ME',
'onClick': {
'action': {
'actionMethodName': 'assign',
},
},
},
},
],
},
],
},
],
}
},
],
};
}
Apps Script
/**
* Responds to messages that have links whose URLs match URL patterns
* configured for link previews.
*
* @param {Object} event The event object from Chat API.
* @return {Object} Response from the Chat app attached to the message with
* the previewed link.
*/
function onMessage(event) {
// Checks for the presence of event.message.matchedUrl and attaches a card
// if present
if (event.message.matchedUrl) {
return {
'actionResponse': {
'type': 'UPDATE_USER_MESSAGE_CARDS',
},
'cardsV2': [{
'cardId': 'previewLink',
'card': {
'header': {
'title': 'Example Customer Service Case',
'subtitle': 'Case basics',
},
'sections': [{
'widgets': [
{'keyValue': {'topLabel': 'Case ID', 'content': 'case123'}},
{'keyValue': {'topLabel': 'Assignee', 'content': 'Charlie'}},
{'keyValue': {'topLabel': 'Status', 'content': 'Open'}},
{
'keyValue': {
'topLabel': 'Subject', 'content': 'It won\'t turn on...',
}
},
],
},
{
'widgets': [{
'buttons': [
{
'textButton': {
'text': 'OPEN CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123',
},
},
},
},
{
'textButton': {
'text': 'RESOLVE CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123?resolved=y',
},
},
},
},
{
'textButton': {
'text': 'ASSIGN TO ME',
'onClick': {'action': {'actionMethodName': 'assign'}}
},
},
],
}],
}],
},
}],
};
}
// If the Chat app doesn’t detect a link preview URL pattern, it says so.
return {'text': 'No matchedUrl detected.'};
}
/**
* Updates a card that was attached to a message with a previewed link.
*
* @param {Object} event The event object from Chat API.
* @return {Object} Response from the Chat app. Either a new card attached to
* the message with the previewed link, or an update to an existing card.
*/
function onCardClick(event) {
// Checks whether the message event originated from a human or a Chat app
// and sets actionResponse to "UPDATE_USER_MESSAGE_CARDS if human or
// "UPDATE_MESSAGE" if Chat app.
const actionResponseType = event.message.sender.type === 'HUMAN' ?
'UPDATE_USER_MESSAGE_CARDS' :
'UPDATE_MESSAGE';
// To respond to the correct button, checks the button's actionMethodName.
if (event.action.actionMethodName === 'assign') {
return assignCase(actionResponseType);
}
}
/**
* Updates a card to say that "You" are the assignee after clicking the Assign
* to Me button.
*
* @param {String} actionResponseType Which actionResponse the Chat app should
* use to update the attached card based on who created the message.
* @return {Object} Response from the Chat app. Updates the card attached to
* the message with the previewed link.
*/
function assignCase(actionResponseType) {
return {
'actionResponse': {
// Dynamically returns the correct actionResponse type.
'type': actionResponseType,
},
'cardsV2': [{
'cardId': 'assignCase',
'card': {
'header': {
'title': 'Example Customer Service Case',
'subtitle': 'Case basics',
},
'sections': [{
'widgets': [
{'keyValue': {'topLabel': 'Case ID', 'content': 'case123'}},
{'keyValue': {'topLabel': 'Assignee', 'content': 'You'}},
{'keyValue': {'topLabel': 'Status', 'content': 'Open'}},
{
'keyValue': {
'topLabel': 'Subject', 'content': 'It won\'t turn on...',
}
},
],
},
{
'widgets': [{
'buttons': [
{
'textButton': {
'text': 'OPEN CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123',
},
},
},
},
{
'textButton': {
'text': 'RESOLVE CASE',
'onClick': {
'openLink': {
'url': 'https://support.example.com/orders/case123?resolved=y',
},
},
},
},
{
'textButton': {
'text': 'ASSIGN TO ME',
'onClick': {'action': {'actionMethodName': 'assign'}},
},
},
],
}],
}],
},
}],
};
}
制限事項と考慮事項
Chat アプリのリンク プレビューを設定する際は、次の制限事項と考慮事項に注意してください。
- 各 Chat アプリでは、最大 5 つの URL パターンのリンク プレビューがサポートされています。
- Chat 用アプリでは、メッセージごとに 1 つのリンクがプレビュー可能。1 つのメッセージにプレビュー可能なリンクが複数存在する場合は、最初のプレビュー可能なリンクのみがプレビューされます。
- Chat アプリでは
https://
で始まるリンクのみがプレビューされるため、https://support.example.com/cases/
ではプレビューされますが、support.example.com/cases/
ではプレビューされません。
- メッセージに、スラッシュ コマンドなど、Chat アプリに送信される他の情報が含まれていない限り、リンク プレビューで Chat アプリに送信されるのはリンク URL のみです。
- プレビュー リンクに添付されるカードは、Chat アプリのインタラクション イベントに応じて、
UPDATE_USER_MESSAGE_CARDS
タイプの ActionResponse
のみをサポートします。リンク プレビューでは、UPDATE_MESSAGE
、または Chat API を介してプレビュー リンクに添付されたカードを更新する非同期リクエストはサポートされていません。詳しくは、カードの更新をご覧ください。
リンク プレビューをデバッグする
リンク プレビューを実装する際には、アプリのログを参照して Chat アプリをデバッグする必要が生じることがあります。ログを確認するには、Google Cloud コンソールのログ エクスプローラにアクセスします。