为防止用户在 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/
)注册为网址格式,以便 Chat 应用可以预览这些链接。
- 打开 Google Cloud Console。
- 点击“Google Cloud”旁边的向下箭头 arrow_drop_down,然后打开 Chat 应用的项目。
- 在搜索字段中,输入
Google Chat API
,然后点击 Google Chat API。
- 点击管理 > 配置。
- 在链接预览下,添加或修改网址格式。
- 要为新网址格式配置链接预览,请点击添加网址格式。
- 若要修改现有网址格式的配置,请点击向下箭头 expand_more。
在主机模式字段中,输入网址格式的网域。Chat 应用会预览指向此网域的链接。
如要为特定子网域(如 subdomain.example.com
)显示 Chat 应用的预览链接,请添加该子网域。
如要为整个网域设置 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 聊天室中,当有人消息中包含与链接预览网址格式相符的链接时,您的 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 应用可以通过对链接发出一条简单的短信来预览链接。此示例会附加一条与链接预览网址格式匹配的链接网址的消息。
附加卡
如需将卡片附加到预览链接,请返回 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) {
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.'};
}
更新银行卡
如需更新附加到预览链接的卡片,请返回类型为 UPDATE_USER_MESSAGE_CARDS
的 ActionResponse
。聊天应用只能更新用于预览链接作为 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
。具体方法如下:
更新卡片的典型原因是响应按钮点击。回想上一部分中附加卡片中的分配给我按钮。下面的完整示例更新了卡片,在用户点击分配给我后,卡片会分配给“您”。该示例通过检查发送者类型来动态设置 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 应用配置链接预览时,请注意以下限制和注意事项:
- 每个 Chat 应用最多支持 5 种网址格式的链接预览。
- Chat 应用在每条消息中预览一个链接。如果单则消息中包含多个可预览的链接,系统只会预览第一个可预览的链接。
- 聊天应用只能预览以
https://
开头的链接,因此可预览以 https://support.example.com/cases/
开头的链接,但 support.example.com/cases/
不会。
- 除非消息包含发送到 Chat 应用的其他信息(例如斜杠命令),否则只有链接网址会通过链接预览发送到 Chat 应用。
- 附加到预览链接的卡片仅支持
UPDATE_USER_MESSAGE_CARDS
类型的 ActionResponse
,并且仅适用于响应 Chat 应用互动事件。链接预览不支持 UPDATE_MESSAGE
,也不支持通过 Chat API 更新附加到预览链接的卡片的异步请求。如需了解详情,请参阅更新卡片。
调试链接预览
实现链接预览时,您可能需要查看 Chat 应用的日志来调试该应用。如需读取日志,请访问 Google Cloud 控制台上的日志浏览器。