本頁面說明如何為 Google Chat 應用程式設定及回應斜線指令。
「斜線指令」是使用者與 Chat 應用程式互動的常見方式。斜線指令也能協助使用者探索及使用 Chat 應用程式的主要功能。
如要使用斜線指令,使用者只要依序輸入斜線 (/
) 和短文字指令 (例如 /about
),即可取得 Chat 應用程式的相關資訊。使用者在 Google Chat 中輸入斜線,就會看到列出 Chat 應用程式可用指令的視窗:
如果使用者傳送的訊息含有斜線指令,只有使用者和 Chat 應用程式看得到該訊息。
如要判斷是否應設定斜線指令,並瞭解如何設計使用者互動,請參閱「定義所有使用者歷程」。
必要條件
Node.js
- 擁有可存取 Google Chat 的 Google Workspace 帳戶。
- Chat 應用程式。如要建構 Chat 應用程式,請按照quickstart操作。
Apps Script
- 擁有可存取 Google Chat 的 Google Workspace 帳戶。
- Chat 應用程式。如要建構 Chat 應用程式,請按照quickstart操作。
Python
- 擁有可存取 Google Chat 的 Google Workspace 帳戶。
- Chat 應用程式。如要建構 Chat 應用程式,請按照quickstart操作。
設定斜線指令
本節說明如何完成下列步驟,設定斜線指令:
為斜線指令命名
斜線指令的名稱是使用者輸入 Chat 訊息來叫用 Chat 應用程式的內容。名稱下方也會顯示簡短說明,提示使用者如何使用指令:
為斜線指令選擇名稱和說明時,請考慮以下建議:
如何為斜線指令命名:
- 使用簡短易懂的描述性字詞或詞組,讓使用者更容易說出指令。例如,使用
/remindMe
,而非/createAReminder
。 - 如果您的指令包含多個字詞,請在第一個字詞中使用全部小寫,並將其他字詞的第一個字母大寫,協助使用者閱讀指令。例如,使用
/updateContact
,不要使用/updatecontact
。 - 考慮為指令使用不重複的名稱或一般名稱。如果您的指令描述的是典型的互動或功能,您可以使用使用者熟悉且預期的常用名稱,例如
/settings
或/feedback
。否則,請嘗試使用不重複的指令名稱,因為如果您的指令名稱與其他 Chat 應用程式相同,使用者就必須透過類似的指令進行篩選,才能找到並使用您的指令。
- 使用簡短易懂的描述性字詞或詞組,讓使用者更容易說出指令。例如,使用
如何說明斜線指令:
- 說明應力求簡短明確,讓使用者知道叫用指令時會看到什麼內容。
- 讓使用者瞭解指令是否有任何格式要求。舉例來說,如果您建立的
/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:介於 1 到 1000 之間的數字,供 Chat 應用程式用來辨識斜線指令並傳回回應。
選用:如果您希望 Chat 應用程式使用對話方塊回應指令,請勾選「Open aDialog」。
點按「儲存」。
Chat 應用程式的斜線指令現已設定完成。
回應斜線指令
當使用者建立含有斜線指令的 Chat 訊息時,您的 Chat 應用程式會收到 MESSAGE
互動事件。事件酬載包含斜線指令相關資訊,包括 slashCommand
和 slashCommandMetadata
欄位。您可以使用這些欄位識別指令 ID 並傳回自訂回應。
以下範例顯示包含斜線指令 /vote
的 MESSAGE
互動事件 JSON 酬載:
{
...
"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 Script
/**
* 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
:在 Google Chat API 中設定斜線指令時指定的指令 ID。runFunction
:這個函式會針對斜線指令建立回應。
選用:透過私人訊息回覆
只有傳送訊息的使用者和收到指令的 Chat 應用程式才能看到包含斜線指令的訊息。如果您將 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 Script
這個範例會傳回資訊卡 JSON,以傳送卡片訊息。您也可以使用 Apps Script 卡片服務。
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!"
}
}
}