本页介绍了如何设置和响应 Google Chat 应用。
斜杠命令是用户调用 Chat 应用。斜杠命令也有助于用户发现和 使用 Chat 应用的主要功能。
若要使用斜杠命令,用户需要输入一条斜杠 (/
),然后输入一个简短的文本命令,
例如 /about
,用于获取有关 Chat 应用的信息。
用户只需在
Google Chat,显示了一个窗口,其中列出了
Chat 应用:
![斜杆 (/) 命令窗口](https://developers.google.cn/static/workspace/chat/images/slash-command-window.png?authuser=6&hl=nl)
当用户发送包含斜杠命令的邮件时,该邮件仅 对用户和 Chat 应用可见。
决定是否应该设置斜杠命令,并了解如何 设计用户互动,请参见 定义所有用户历程。
前提条件
Node.js
一款已启用互动功能的 Google Chat 应用。要创建 交互式 Chat 应用,请完成此快速入门。
Apps 脚本
一款已启用互动功能的 Google Chat 应用。要创建 交互式聊天应用,请完成此快速入门。
Python
一款已启用互动功能的 Google Chat 应用。要创建 交互式 Chat 应用,请完成此快速入门。
设置斜杠命令
本部分介绍了如何完成以下步骤来设置斜杠 命令:
为斜杠命令命名
斜杠命令的名称是用户在 Chat 消息中输入的内容 来调用 Chat 应用。简短说明 ,以进一步提示用户如何使用此命令:
![斜杆 (/) 命令名称和说明](https://developers.google.cn/static/workspace/chat/images/slash-command-description.png?authuser=6&hl=nl)
为斜杠命令选择名称和说明时,请考虑 以下建议:
如需为斜杠命令命名,请执行以下操作:
- 请使用简短、说明性并且有实际指导意义的字词或词组,
命令简单明了。例如,不要使用
/createAReminder
,请使用/remindMe
。 - 如果您的命令包含多个字词,请帮助用户阅读命令
将第一个单词全部小写,然后将第一个单词的首字母大写,
其他单词的字母。例如,不要使用
/updatecontact
, 请使用/updateContact
。 - 请考虑为您的命令使用唯一名称还是通用名称。如果
您的命令描述了典型的交互或功能,那么,您可以使用
用户认可和期望的常用名称,例如
/settings
或/feedback
。否则,请尝试使用唯一的命令名称,因为如果您的 命令名称相同,用户必须 以便查找和使用你的命令
- 请使用简短、说明性并且有实际指导意义的字词或词组,
命令简单明了。例如,不要使用
如需描述您的斜杠命令,请执行以下操作:
- 尽量提供简洁明了的说明,以便用户了解预期结果 它们会调用命令
- 告知用户该命令是否有任何格式设置要求。
例如,如果您创建了一个需要参数的
/remindMe
命令, 请将说明设置为类似Remind me to do [something] at [time]
的内容。 - 告知用户 Chat 应用是否会回复
也可以只与调用相应命令的用户私下分享
例如,对于斜杠命令
/about
,您可以将其描述为Learn about this app (Only visible to you)
。要私下回复 斜杠命令,请参阅回复私信部分。
在 Google Chat API 中配置斜杠命令
要创建斜杠命令,您需要在 您的 Chat 应用的 Google Chat API 配置。
如需在 Google Chat API 中配置斜杠命令,请完成以下操作 步骤:
在 Google Cloud 控制台中,点击“菜单” > API 和服务 > 已启用的 API 和服务 > Google Chat API
点击配置。
在斜杠命令下,点击添加斜杠命令。
为该命令输入名称、命令 ID 和说明:
- 名称:命令的显示名称以及用户输入的内容 来调用您的应用必须以斜杠开头、只能包含文字,并且不能 不能超过 50 个字符。
- 说明:说明如何使用和设置格式的文本 命令。说明最多可包含 50 个字符。
- 命令 ID:在命令 Chat 应用用于识别斜杠命令 并返回响应。
可选:如果您希望 Chat 应用回复 使用对话框执行命令,请选择 打开对话框复选框。
点击保存。
现已为 Chat 应用配置斜杠命令。
响应斜杠命令
如果用户创建包含斜杠命令的 Chat 消息,
您的 Chat 应用收到 MESSAGE
互动事件。
事件载荷包含斜杠命令的相关信息
包括 slashCommand
和slashCommandMetadata
字段。您可以使用这些字段来识别命令 ID 并返回自定义
响应。
以下示例展示了 MESSAGE
互动事件的 JSON 载荷
(包含斜杠命令 /vote
):
{
...
"message": {
...
"text": "/vote yes",
"argumentText": " yes",
"slashCommand": {
"commandId": 2
},
"annotations": [
{
"length": 5,
"startIndex": 0,
"type": "SLASH_COMMAND",
"slashCommand": {
"commandName":"/vote",
"commandId":1,
"type": "INVOKE",
"bot": {
"avatarUrl": "https://www.example.com/images/vote-app-icon.png",
"displayName": "Voter Chat App",
"name": "users/1234567890987654321",
"type": "BOT"
}
}
}
]
}
}
如需响应斜杠命令,您可以检测 slashCommand
字段
是否存在,如果是,则返回对命令的响应。
以下代码示例展示了如何响应 MESSAGE
互动事件
包含斜杠命令:
Node.js
/**
* Responds to a MESSAGE event in Google Chat.
*
* @param {Object} event the event object from Chat API.
*
* @return {object} function in response to a slash command.
*/
exports.onMessage = function onMessage(req, res) {
// Stores the Google Chat event as a variable.
var event = req.body;
// Checks for the presence of event.message.slashCommand.
if (event.message.slashCommand) {
switch (event.message.slashCommand.commandId) {
case ID: // The ID for your slash command
res.json(runFunction); // The response to the slash command.
}
}
Apps 脚本
/**
* Responds to a MESSAGE event in Google Chat.
*
* @param {Object} event the event object from Chat API.
*
* @return {object} function in response to a slash command.
*/
function onMessage(event) {
// Checks for the presence of event.message.slashCommand
if (event.message.slashCommand) {
switch (event.message.slashCommand.commandId) {
case ID: // The ID for your slash command
return runFunction; // The response to the slash command.
}
}
}
Python
from typing import Any, Mapping
import flask
import functions_framework
@functions_framework.http
def main(req: flask.Request) -> Mapping[str, Any]:
"""Responds to a MESSAGE event in Google Chat that includes a slash command.
Args:
req (flask.Request): the event object from Chat API.
Returns:
Mapping[str, Any]: function in response to a slash command.
"""
if req.method == 'GET':
return 'Sorry, this function must be called from a Google Chat.'
request = req.get_json(silent=True)
if slash_command := request.get('message', dict()).get('slashCommand'):
command_id = slash_command['commandId']
if command_id == ID:
return runFunction
如需使用该代码,请替换以下内容:
ID
:您在创建命令时指定的命令 ID 在 Google Chat API 中配置斜杠命令。runFunction
:一个函数,用于创建 斜杠命令
可选:回复私信
包含斜杠命令的邮件仅对发送 消息以及接收命令的 Chat 应用。如果 您已将 Chat 应用配置为添加到聊天室 则可以考虑响应斜杠命令 从而确保用户和 Chat 应用。
例如,如果某个团队使用一款聊天应用来管理
客户服务,用户可以调用斜杠命令,例如
/myCases
,可查看为其分配的支持请求。如果该团队将
将 Chat 扩展应用添加到聊天室(使用此斜杠命令的用户)
可能希望 Chat 应用仅回应
。为避免向聊天室中的所有人发布用户的支持请求,
Chat 应用可以私下回复。
要以非公开方式响应斜杠命令,请参阅 向 Google Chat 用户发送私信。
完整示例:使用 Rolodex Chat 应用设置联系人
以下示例展示了一个聊天应用 以下斜杠命令:
/help
命令会返回一条文本消息,说明如何获取 Chat 应用提供支持。命令 ID 已设置 至1
。/createContact
命令会打开一个对话框,用户可以在其中输入 某个联系人的详细信息。命令 ID 设置为2
。
在运行此示例之前,请按以下步骤 在 Google Chat API 中配置斜杠命令。
Node.js
/**
* 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.
*/
exports.onMessage = function onMessage(req, res) {
// Store the Google Chat event as a variable.
const event = req.body;
if (req.method === "GET" || !event.message) {
res.send("Hello! This function is meant to be used in a Google Chat " +
"Space.");
}
// Checks for the presence of event.message.slashCommand.
// If the slash command is "/help", responds with a text message.
// If the slash command is "/createContact", opens a dialog.
if (event.message.slashCommand) {
switch (event.message.slashCommand.commandId) {
case 1: // /help
res.json({"text": "Contact bot helps you update your address book!"});
case 2: // /createContact
res.json(openDialog(event));
}
}
// If the Chat app doesn"t detect a slash command, it responds
// with a card that prompts the user to add a contact
else {
res.json({
"cardsV2": [{
"cardId": "addContact",
"card": {
"header": {
"title": "Rolodex",
"subtitle": "Manage your contacts!",
"imageUrl": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
"imageType": "CIRCLE"
},
"sections": [
{
"widgets": [
{
"buttonList": {
"buttons": [
{
"text": "Add Contact",
"onClick": {
"action": {
"function": "openDialog",
"interaction": "OPEN_DIALOG"
}
}
}
]
}
}
]
}
]
}
}]
});
}
// Respond to button clicks on attached cards
if (event.type === "CARD_CLICKED") {
if (event.common.invokedFunction === "openDialog") {
res.json(openDialog(event));
}
if (event.common.invokedFunction === "openSequentialDialog") {
res.json(openSequentialDialog(event));
}
if (event.common.invokedFunction === "confirmDialogSuccess") {
res.json(confirmDialogSuccess(event));
}
}
};
/**
* Opens and starts a dialog that lets users add details about a contact.
*
* @param {object} event the event object from Google Chat.
*
* @return {object} open a dialog.
*/
function openDialog(event) {
return {
"action_response": {
"type": "DIALOG",
"dialog_action": {
"dialog": {
"body": {
"sections": [
{
"header": "Add new contact",
"widgets": [
{
"textInput": {
"label": "Name",
"type": "SINGLE_LINE",
"name": "name"
}
},
{
"textInput": {
"label": "Address",
"type": "MULTIPLE_LINE",
"name": "address"
}
},
{
"decoratedText": {
"text": "Add to favorites",
"switchControl": {
"controlType": "SWITCH",
"name": "saveFavorite"
}
}
},
{
"decoratedText": {
"text": "Merge with existing contacts",
"switchControl": {
"controlType": "SWITCH",
"name": "mergeContact",
"selected": true
}
}
},
{
"buttonList": {
"buttons": [
{
"text": "Next",
"onClick": {
"action": {
"function": "openSequentialDialog"
}
}
}
]
}
}
]
}
]
}
}
}
}
};
};
/**
* Opens a second dialog that lets users add more contact details.
*
* @param {object} event the event object from Google Chat.
*
* @return {object} open a dialog.
*/
function openSequentialDialog(event) {
return {
"action_response": {
"type": "DIALOG",
"dialog_action": {
"dialog": {
"body": {
"sections": [
{
"header": "Add new contact",
"widgets": [
{
"textInput": {
"label": "Notes",
"type": "MULTIPLE_LINE",
"name": "notes"
}
},
{
"selectionInput": {
"type": "RADIO_BUTTON",
"label": "Contact type",
"name": "contactType",
"items": [
{
"text": "Work",
"value": "Work",
"selected": false
},
{
"text": "Personal",
"value": "Personal",
"selected": false
}
]
}
},
{
"buttonList": {
"buttons": [
{
"text": "Submit",
"onClick": {
"action": {
"function": "confirmDialogSuccess",
"parameters": [
{
"key": "confirmDialogSuccess",
"value": "confirmDialogSuccess"
}
]
}
}
}
]
},
"horizontalAlignment": "END"
}
]
}
]
}
}
}
}
};
}
/**
* Checks for a form input error, the absence of
* a "name" value, and returns an error if absent.
* Otherwise, confirms successful receipt of a dialog.
*
* Confirms successful receipt of a dialog.
*
* @param {Object} event the event object from Chat API.
*
* @return {object} open a Dialog in Google Chat.
*/
function receiveDialog(event) {
// Checks to make sure the user entered a name
// in a dialog. If no name value detected, returns
// an error message.
if (event.common.formInputs.contactName.stringInputs.value[0] === "") {
return {
"actionResponse": {
"type": "DIALOG",
"dialogAction": {
"actionStatus": {
"statusCode": "OK",
"userFacingMessage": "Don't forget to name your new contact!"
}
}
}
};
// Otherwise the app indicates that it received
// form data from the dialog. Any value other than "OK"
// gets returned as an error. "OK" is interpreted as
// code 200, and the dialog closes.
} else {
return {
"actionResponse": {
"type": "DIALOG",
"dialogAction": {
"actionStatus": "OK"
}
}
};
}
}
Apps 脚本
此示例通过返回 卡片 JSON。 您还可以使用 Apps 脚本卡片服务。
Python
from typing import Any, Mapping
import flask
import functions_framework
@functions_framework.http
def main(req: flask.Request) -> Mapping[str, Any]:
"""Responds to a MESSAGE event in Google Chat that includes the /createContact
slash command by opening a dialog.
Args:
req (flask.Request): the event object from Chat API.
Returns:
Mapping[str, Any]: open a Dialog in response to a card's button click.
"""
if req.method == 'GET':
return 'Sorry, this function must be called from a Google Chat.'
request = req.get_json(silent=True)
if request.get('type') == 'CARD_CLICKED':
invoked_function = request.get('common', dict()).get('invokedFunction')
if invoked_function == 'open_dialog':
return open_dialog(request)
elif invoked_function == 'open_sequential_dialog':
return open_dialog(request)
elif invoked_function == "receive_dialog":
return receive_dialog(request)
else:
return {
'cardsV2': [{
'cardId': 'addContact',
'card': {
'header': {
'title': 'Rolodex',
'subtitle': 'Manage your contacts!',
'imageUrl': 'https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png',
'imageType': 'CIRCLE'
},
'sections': [
{
'widgets': [
{
'buttonList': {
'buttons': [
{
'text': 'Add Contact',
'onClick': {
'action': {
'function': 'open_dialog',
'interaction': 'OPEN_DIALOG'
}
}
}
]
}
}
]
}
]
}
}]
}
def open_dialog(request: Mapping[str, Any]) -> Mapping[str, Any]:
"""Opens a dialog in Google Chat.
Args:
request (Mapping[str, Any]): the event object from Chat API.
Returns:
Mapping[str, Any]: open a Dialog in response to a card's button click.
"""
return {
'action_response': {
'type': 'DIALOG',
'dialog_action': {
'dialog': {
'body': {
'sections': [
{
'header': 'Add new contact',
'widgets': [
{
'textInput': {
'label': 'Name',
'type': 'SINGLE_LINE',
'name': 'name'
}
},
{
'textInput': {
'label': 'Address',
'type': 'MULTIPLE_LINE',
'name': 'address'
}
},
{
'decoratedText': {
'text': 'Add to favorites',
'switchControl': {
'controlType': 'SWITCH',
'name': 'saveFavorite'
}
}
},
{
'decoratedText': {
'text': 'Merge with existing contacts',
'switchControl': {
'controlType': 'SWITCH',
'name': 'mergeContact',
'selected': True
}
}
},
{
'buttonList': {
'buttons': [
{
'text': 'Next',
'onClick': {
'action': {
'function': 'open_sequential_dialog'
}
}
}
]
}
}
]
}
]
}
}
}
}
}
def open_sequential_dialog(request: Mapping[str, Any]) -> Mapping[str, Any]:
"""Opens a second dialog that lets users add more contact details.
Args:
request (Mapping[str, Any]): the event object from Chat API.
Returns:
Mapping[str, Any]: open a Dialog in response to a card's button click.
"""
return {
'action_response': {
'type': 'DIALOG',
'dialog_action': {
'dialog': {
'body': {
'sections': [
{
'header': 'Add new contact',
'widgets': [
{
'textInput': {
'label': 'Notes',
'type': 'MULTIPLE_LINE',
'name': 'notes'
}
},
{
'selectionInput': {
'type': 'RADIO_BUTTON',
'label': 'Contact type',
'name': 'contactType',
'items': [
{
'text': 'Work',
'value': 'Work',
'selected': False
},
{
'text': 'Personal',
'value': 'Personal',
'selected': False
}
]
}
},
{
'buttonList': {
'buttons': [
{
'text': 'Submit',
'onClick': {
'action': {
'function': 'receive_dialog',
'parameters': [
{
'key': 'receiveDialog',
'value': 'receiveDialog'
}
]
}
}
}
]
},
'horizontalAlignment': 'END'
}
]
}
]
}
}
}
}
}
def receive_dialog(event: Mapping[str, Any]) -> Mapping[str, Any]:
"""Checks for a form input error, the absence of a "name" value, and returns
an error if absent. Otherwise, confirms successful receipt of a dialog.
Args:
event (Mapping[str, Any]): the event object from Chat API.
Returns:
Mapping[str, Any]: the response.
"""
if event.get('common', dict()) \
.get('formInputs', dict()).get('contactName', dict()) \
.get('stringInputs').get('value', list()):
return {
'actionResponse': {
'type': 'DIALOG',
'dialogAction': {
'actionStatus': 'OK'
}
}
}
else:
return {
'actionResponse': {
'type': 'DIALOG',
'dialogAction': {
'actionStatus': "Don't forget to name your new contact!"
}
}
}