为了防止在用户分享 Google Chat 中的链接时切换上下文,您的 Chat 应用可在消息中附加一张卡片来预览链接,从而提供更多信息,让用户直接在 Google Chat 中执行操作。
例如,假设有一个 Google Chat 聊天室,其中包含一家公司的所有客服人员以及一款名为 Case-y 的 Chat 应用。客服人员经常在 Chat 聊天室中分享客户服务支持请求的链接,他们每次处理支持请求时,同事都必须打开支持请求链接,查看分配对象、状态和主题等详细信息。同样,如果有人想获得案例的所有权或更改状态,则需要打开链接。
借助链接预览功能,聊天室的常驻 Chat 扩展应用 Case-y 可以在有人分享支持请求链接时附加一张显示分配对象、状态和主题的卡片。通过卡片上的按钮,客服人员可以直接在聊天信息流中接管支持请求并更改状态。
链接预览功能的运作方式
当有人将链接添加到自己的消息中时,系统会显示一个条状标签,告知他们 Chat 应用可能会预览链接。
用户发送消息后,系统会将链接发送给 Chat 应用,然后应用会生成卡片并将其附加到用户的消息中。
除了链接之外,卡片还提供了有关链接的更多信息,包括按钮等互动元素。您的 Chat 应用可以更新附加的卡片,以响应用户互动(例如点击按钮)。
如果有人不希望 Chat 应用通过在消息中附加卡片来预览链接,可以点击预览条状标签上的 cancel 来阻止预览。用户可以随时点击移除预览来移除附加的卡片。
前提条件
Node.js
一款已启用互动功能的 Google Chat 应用。要创建
交互式 Chat 应用,请完成此快速入门。
Apps 脚本
一款已启用互动功能的 Google Chat 应用。要创建
交互式聊天应用,请完成此快速入门。
在 Google Cloud 控制台中,将 example.com
、support.example.com
和 support.example.com/cases/
等特定链接注册为网址格式,以便 Chat 应用能够预览这些链接。
- 打开 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
。此示例中附加了一张简单的卡片。
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 应用必须为聊天室中的所有人预览链接,因此
该消息必须省略
privateMessageViewer
字段。
调试链接预览
在实现链接预览时,您可能需要通过读取 Chat 应用的日志来调试该应用。如需读取日志,请访问 Google Cloud 控制台上的日志浏览器。