为了防止在用户分享 Google Chat 中的链接时切换上下文,您的 Chat 应用可在消息中附加一张卡片来预览链接,从而提供更多信息,让用户直接在 Google Chat 中执行操作。
例如,假设有一个 Google Chat 聊天室,其中包含一家公司的所有客户服务代理,还有一款名为 Case-y 的 Chat 应用。客服人员经常在 Chat 聊天室中分享客户服务支持请求的链接,他们每次处理支持请求时,同事都必须打开支持请求链接,查看分配对象、状态和主题等详细信息。同样,如果有人想获得案例的所有权或更改状态,则需要打开链接。
借助链接预览功能,聊天室的常驻 Chat 应用 Case-y 可以在有人分享支持请求链接时附加一张显示分配对象、状态和主题的卡片。通过卡片上的按钮,客服人员可以直接在聊天信息流中接管支持请求并更改状态。
链接预览功能的运作方式
当有人将链接添加到自己的消息中时,系统会显示一个条状标签,告知他们 Chat 应用可能会预览链接。
![表示 Chat 应用可以预览链接的条状标签](https://google-developers.gonglchuangl.net/static/workspace/chat/images/link-preview-chip.png?authuser=0&hl=sr)
用户发送消息后,系统会将链接发送给 Chat 应用,然后应用会生成卡片并将其附加到用户的消息中。
![Chat 应用通过在消息中附加卡片来预览链接](https://google-developers.gonglchuangl.net/static/workspace/chat/images/link-preview-case-details.png?authuser=0&hl=sr)
除了链接之外,卡片还提供了有关链接的更多信息,包括按钮等互动元素。您的 Chat 应用可以更新附加的卡片,以响应用户互动(例如点击按钮)。
如果有人不希望 Chat 应用通过在消息中附加卡片来预览链接,可以点击预览条状标签上的 cancel 来阻止预览。用户可以随时点击移除预览来移除附加的卡片。
前提条件
Node.js
一款已启用互动功能的 Google Chat 应用。要创建
交互式 Chat 应用,请完成此快速入门。
Apps 脚本
一款已启用互动功能的 Google Chat 应用。要创建
交互式聊天应用,请完成此快速入门。
在 Google Cloud 控制台中,将 example.com
、support.example.com
和 support.example.com/cases/
等特定链接注册为网址格式,以便 Chat 应用能够预览这些链接。
![链接预览配置菜单](https://google-developers.gonglchuangl.net/static/workspace/chat/images/link-preview-configuration.png?authuser=0&hl=sr)
- 打开 Google Cloud Console。
- 在“Google Cloud”旁边点击向下箭头 arrow_drop_down,然后打开 Chat 应用的项目。
- 在搜索字段中输入
Google Chat API
,然后点击 Google Chat API。
- 点击管理 > 配置。
- 在“链接预览”下,添加或修改网址格式。
- 要为新网址格式配置链接预览,请点击添加网址格式。
- 要修改现有网址格式的配置,请点击向下箭头 expand_more。
在主机格式字段中,输入网址格式的域名。Chat 应用会预览指向此网域的链接。
如要让 Chat 扩展应用预览特定子网域(例如 subdomain.example.com
)的链接,请添加该子网域。
如要让 Chat 应用预览整个网域的链接,请指定以星号 (*) 作为子网域的通配符。例如,*.example.com
与 subdomain.example.com
和 any.number.of.subdomains.example.com
匹配。
在路径前缀字段中,输入要附加到主机模式域名的路径。
如要匹配主机模式网域中的所有网址,请将路径前缀留空。
例如,如果主机模式为 support.example.com
,要匹配在 support.example.com/cases/
托管的支持请求的网址,请输入 cases/
。
点击完成。
点击保存。
现在,如果有人在包含您的 Chat 应用的 Chat 聊天室中的消息中添加链接预览网址格式,那么您的应用就会预览该链接。
预览链接
为给定链接配置链接预览后,您的
Chat 应用可以通过以下方式识别和预览链接:
并附上更多信息
包含您
Chat 应用(如果某人的消息包含
与链接预览网址格式匹配,那么您的 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 应用可以执行以下操作:
您既可以使用简单的短信进行回复,也可以附上 卡片。
使用文本消息回复
对于简单回复,您的 Chat 应用可以预览链接
通过使用简单的短信进行回复
链接。此示例中附加了一封邮件,
与链接预览网址格式匹配。
附加卡
如需将卡片附加到预览链接,请按以下步骤操作:
返回一个
ActionResponse
类型为 UPDATE_USER_MESSAGE_CARDS
。此示例中附加了一张简单的卡片。
![Chat 应用通过在消息中附加卡片来预览链接](https://google-developers.gonglchuangl.net/static/chat/images/link-preview-case-details.png?authuser=0&hl=sr)
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) {
return 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) {
return 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.
return res.json({'text': 'No matchedUrl detected.'});
};
Apps 脚本
此示例通过返回
卡片 JSON。
您还可以使用
Apps 脚本卡片服务。
/**
* 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.'};
}
更新银行卡
要更新附加到预览链接的卡片,请将
ActionResponse
类型为 UPDATE_USER_MESSAGE_CARDS
。聊天应用只能更新
这些卡片可预览
Chat 应用互动事件。
Chat 应用无法通过调用 Chat API 更新这些卡片
异步执行。
链接预览不支持返回 UPDATE_MESSAGE
类型的 ActionResponse
。由于 UPDATE_MESSAGE
会更新整条消息,而不仅仅是卡片,因此只有在 Chat 应用创建原始消息的情况下,该功能才会起作用。链接预览会将卡片附加到用户创建的消息中,因此 Chat 应用无权更新卡片。
为了确保某个函数会同时更新聊天信息流中用户创建的卡片,请根据消息是由 Chat 应用还是用户创建的,动态设置ActionResponse
。
为此,您可以采用两种方法:在附加卡片的 onclick
属性中指定并检查自定义 actionMethodName
(用于将消息标识为由用户创建),或检查消息是否由用户创建。
如需使用 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 的完整代码。Case-y 是一款 Chat 扩展应用,用于预览在客户服务客服人员协作的 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) {
return res.send(
'Hello! This function is meant to be used in a Google Chat Space.');
}
// 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') {
return res.json(createMessage(actionResponseType, 'You'));
}
}
// Checks for the presence of event.message.matchedUrl and attaches a card
// if present
if (req.body.message.matchedUrl) {
return res.json(createMessage());
}
// If the Chat app doesn’t detect a link preview URL pattern, it says so.
return 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 脚本
此示例通过返回
卡片 JSON。
您还可以使用
Apps 脚本卡片服务。
/**
* 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 应用配置链接预览时,请注意以下限制和注意事项:
- 每个聊天应用最多支持 5 种网址格式的链接预览。
- Chat 应用在每条消息中会预览一个链接。如果一条消息中存在多个可预览的链接,则只有第一个可预览链接可以预览。
- Chat 扩展应用只会预览以
https://
开头的链接,因此 https://support.example.com/cases/
会预览,但 support.example.com/cases/
不会。
- 除非消息包含发送到 Chat 应用的其他信息(例如斜杠命令),否则只有链接网址会通过链接预览发送到 Chat 应用。
- 附加到预览链接的卡片仅支持
UPDATE_USER_MESSAGE_CARDS
类型的 ActionResponse
,并且仅用于响应 Chat 应用互动事件。链接预览不支持通过 Chat API 更新预览链接所附卡片的 UPDATE_MESSAGE
或异步请求。如需了解详情,请参阅更新卡片。
调试链接预览
在实现链接预览时,您可能需要通过读取 Chat 应用的日志来调试该应用。如需读取日志,请访问 Google Cloud 控制台上的日志浏览器。