На этой странице описано, как ваше приложение чата может открывать диалоги и отвечать на них.
Диалоги представляют собой оконные интерфейсы на основе карточек , которые приложения чата открывают для взаимодействия с пользователями. Чтобы помочь пользователям выполнять многоэтапные процессы, приложение Chat может открывать последовательные диалоги.
Диалоги полезны для следующих типов взаимодействия с пользователем:
- Сбор информации от пользователей
- Аутентификация пользователей с помощью веб-сервисов
- Настройка параметров приложения чата
Для открытия, отправки или отмены диалога требуется синхронный ответ от приложения чата с DialogEventType
. Диалоги не поддерживаются приложениями чата, созданными с асинхронной архитектурой , такими как Pub/Sub или метод create
сообщения . Если ваше приложение Chat использует асинхронную архитектуру, используйте сообщение-карточку вместо диалога.
Предварительные условия
Node.js
Приложение Google Chat с интерактивными функциями. Чтобы создать интерактивное приложение чата с использованием службы HTTP, выполните это краткое руководство .Скрипт приложений
Приложение Google Chat с интерактивными функциями. Чтобы создать интерактивное приложение чата с помощью Apps Script, выполните это краткое руководство .Питон
Приложение Google Chat с интерактивными функциями. Чтобы создать интерактивное приложение чата с использованием службы HTTP, выполните это краткое руководство .Открыть диалог
Ваше приложение Chat может открыть диалоговое окно в ответ на нажатие пользователем кнопки в сообщении-карточке .
![Нажатие кнопки на карточке открывает диалоговое окно.](https://developers-dot-devsite-v2-prod.appspot.com/static/workspace/chat/images/dialogs-open-card-click.png?authuser=0000&hl=ru)
Ваше приложение Chat может открыть диалоговое окно в ответ на команду, вводимую пользователем.
![Нажатие кнопки на карточке открывает диалоговое окно.](https://developers-dot-devsite-v2-prod.appspot.com/static/workspace/chat/images/dialogs-open-slash-command.png?authuser=0000&hl=ru)
Когда пользователь открывает диалоговое окно, ваше приложение Chat получает событие взаимодействия со следующей информацией:
-
isDialogEvent
имеетtrue
. DialogEventType
указывает одно из следующих действий, предпринятых пользователем:-
REQUEST_DIALOG
: открыл диалог. -
SUBMIT_DIALOG
: нажал кнопку в диалоговом окне. -
CANCEL_DIALOG
: Отменен диалог.
-
Например, когда пользователь открывает диалоговое окно, ваше приложение Chat получает событие взаимодействия, подобное следующему:
JSON
{
"type": enum (EventType),
"eventTime": string,
"threadKey": string,
"message": {
object (Message)
},
"user": {
object (User)
},
"space": {
object (Space)
},
"action": {
object (FormAction)
},
"configCompleteRedirectUrl": string,
"isDialogEvent": true,
"dialogEventType": "REQUEST_DIALOG",
"common": {
object (CommonEventObject)
}
}
Ваше приложение чата может открыть диалог, вернув ActionResponse
"type": "DIALOG"
с DialogAction
, который включает JSON-описание диалога:
JSON
{
"action_response": {
"type": "DIALOG",
"dialog_action": {
"dialog": {
"body": {
"sections": [
{
"header": "Add new contact",
"widgets": [
{
"textInput": {
"label": "Name",
"type": "SINGLE_LINE",
"name": "contactName"
}
},
{
"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"
}
}
}
]
}
}
]
}
]
}
}
}
}
}
Открытие диалога в ответ на нажатие кнопки карточки
Чтобы кнопка карточки открывала диалог, укажите следующее:
-
onClick.action.function
— имя функции, открывающей диалоговое окно. -
onClick.action.interaction
какOPEN_DIALOG
. Это свойство сообщает Chat, что приложение Chat хочет открыть диалог.
Когда пользователь нажимает кнопку на карточке, ваше приложение Chat получает событие взаимодействия со следующей информацией:
-
EventType
—CARD_CLICKED
. -
DialogEventType
—REQUEST_DIALOG
. -
common.invokedFunction
— это имя функции из свойстваonClick
нажатой кнопки карточки.
Чтобы открыть диалог, ответьте следующим образом:
-
ActionResponse
типа"type": "DIALOG"
. -
DialogAction
, включающий JSON-описание диалога.
В этом примере приложение чата отвечает на событие взаимодействия MESSAGE
карточкой с кнопкой, открывающей диалоговое окно:
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.");
}
// 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") {
openDialog(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) {
res.json({
"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"
}
}
}
]
}
}
]
}
]
}
}
}
}
});
};
}
}
Скрипт приложений
В этом примере отправляется карточное сообщение, возвращая card JSON . Вы также можете использовать службу карточек Apps Script .
/**
* Responds to a MESSAGE event in Google Chat with a card with a button
* that opens a dialog.
*
* @param {Object} event the event object from Chat API.
*
* @return {object} open a Dialog in response to a card's button click.
*/
function onMessage(event) {
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": "openDialog",
"interaction": "OPEN_DIALOG"
}
}
}
]
},
"horizontalAlignment": "CENTER"
}
]
}
]
}
}]
};
}
/**
* Responds to a CARD_CLICKED event in Google Chat.
*
* @param {Object} event the event object from Google Chat
*/
function onCardClick(event) {
if (event.common.invokedFunction === "openDialog") {
return openDialog(event);
}
}
/**
* Opens a dialog in Google Chat.
*
* @param {Object} event the event object from Chat API.
*
* @return {object} open a Dialog in Google Chat.
*/
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": "contactName"
}
},
{
"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"
}
}
}
]
}
}
]
}
]
}
}
}
}
};
}
Питон
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':
if request.get('common', dict()).get('invokedFunction') == 'open_dialog':
return open_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'
}
}
}
]
}
}
]
}
]
}
}
}
}
}
Открыть диалоговое окно в ответ на команду косой черты
Когда пользователь открывает диалоговое окно с помощью косой черты, настроенной для открытия диалогового окна , ваше приложение Chat получает событие взаимодействия со следующей информацией:
-
EventType
—MESSAGE
. -
DialogEventType
—REQUEST_DIALOG
.
Чтобы открыть диалоговое окно, необходимо настроить команду косой черты, установив флажок Открыть диалоговое окно . Затем ответьте следующим образом:
-
ActionResponse
типа"type": "DIALOG"
. -
DialogAction
, включающий JSON-описание диалога.
В этом примере приложение чата реагирует на команду /createContact
, открывая диалоговое окно:
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
openDialog(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) {
res.json({
"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"
}
}
}
]
}
}
]
}
]
}
}
}
}
});
};
Скрипт приложений
В этом примере отправляется карточное сообщение, возвращая card JSON . Вы также можете использовать службу карточек Apps Script .
/**
* Responds to a MESSAGE event in Google Chat that includes the /createContact
* slash command by opening a dialog.
*
* @param {Object} event the event object from Chat API.
*
* @return {object} open a Dialog in response to a slash command.
*/
function onMessage(event) {
// 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
return {"text": "Contact bot helps you update your address book!"}
case 2: // /createContact
return openDialog(event);
}
}
}
/**
* Opens a dialog in Google Chat.
*
* @param {Object} event the event object from Chat API.
*
* @return {object} open a Dialog in Google Chat.
*/
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": "contactName"
}
},
{
"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"
}
}
}
]
}
}
]
}
]
}
}
}
}
};
}
Питон
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 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 == 1:
return {'text': 'Contact bot helps you update your address book!'}
elif command_id == 2:
return open_dialog(request)
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 slash command.
"""
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'
}
}
}
]
}
}
]
}
]
}
}
}
}
}
Открытие последовательных диалогов
Если для взаимодействия с пользователем требуется более одного диалога, вы можете открыть другой диалог, вернув следующий диалог в последовательности в ответ на SUBMIT_DIALOG
DialogEventType
.
![Диалог с множеством различных виджетов.](https://developers-dot-devsite-v2-prod.appspot.com/static/workspace/chat/images/dialogs-card-1.png?authuser=0000&hl=ru)
В кнопке карточки, которая обновляет диалоговое окно, верните onClick.action.function
в качестве имени функции, которая открывает следующее диалоговое окно, и оставьте onClick.action.interaction
неуказанным.
![Диалог с множеством различных виджетов.](https://developers-dot-devsite-v2-prod.appspot.com/static/workspace/chat/images/dialogs-card-2.png?authuser=0000&hl=ru)
По завершении приложение Chat получает значения, введенные пользователями в диалоговом окне, в формате JSON. Сообщите пользователям об успешном взаимодействии , ответив текстовым сообщением или карточкой .
Когда пользователи нажимают кнопку в диалоговом окне, ваше приложение Chat получает событие взаимодействия со следующей информацией:
-
EventType
—CARD_CLICKED
. -
DialogEventType
—SUBMIT_DIALOG
.
В этом примере приложение чата реагирует на событие взаимодействия CARD_CLICKED
нажатием кнопки диалогового окна, открывая другое диалоговое окно:
Node.js
// Respond to button clicks on attached cards
if (event.type === "CARD_CLICKED") {
// Open the first dialog.
if (event.common.invokedFunction === "openDialog") {
openDialog(event);
}
// Open the second dialog.
if (event.common.invokedFunction === "openSequentialDialog") {
openSequentialDialog(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) {
res.json({
"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) {
res.json({
"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"
}
]
}
]
}
}
}
}
});
}
Скрипт приложений
В этом примере отправляется карточное сообщение, возвращая card JSON . Вы также можете использовать службу карточек Apps Script .
/**
* Responds to a CARD_CLICKED event in Google Chat.
*
* @param {Object} event the event object from Google Chat
*/
function onCardClick(event) {
// When a user clicks a card, the Chat app checks to see which function to run.
if (event.common.invokedFunction === "openDialog") {
return openDialog(event);
}
if (event.common.invokedFunction === "openSequentialDialog") {
return openSequentialDialog(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": "contactName"
}
},
{
"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": {
// Specifies which function to run
// in response to the card click.
"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": {
// Specifies which function to run
// in response to the card click.
"function": "receiveDialog",
"parameters": [
{
"key": "receiveDialog",
"value": "receiveDialog"
}
]
}
}
}
]
},
"horizontalAlignment": "END"
}
]
}
]
}
}
}
}
};
}
Питон
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':
if 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)
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': 'receiveDialog',
'parameters': [
{
'key': 'receiveDialog',
'value': 'receiveDialog'
}
]
}
}
}
]
},
'horizontalAlignment': 'END'
}
]
}
]
}
}
}
}
}
Открытие диалогового окна в ответ на взаимодействие на главной странице приложения.
Только для домашнего приложения используйте render_actions
вместо action_response
, чтобы открыть диалоговое окно:
Скрипт приложений
В этом примере отправляется карточное сообщение, возвращая card JSON . Вы также можете воспользоваться карточной службой Apps Script .
function openDialog() {
return {
render_actions: {
action: {
navigations: [{
update_card: {
"sections": [
{
"header": "Add new contact",
"widgets": [
{
"textInput": {
"label": "Name",
"type": "SINGLE_LINE",
"name": "contactName"
}
},
{
"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"
}
}
}
]
}
}]
}]
}
}]
}
}
}
}
Получение данных формы из диалогов
Когда пользователи нажимают кнопку в диалоговом окне, введенные ими данные передаются в приложение Chat, а ваше приложение Chat получает событие взаимодействия со следующей информацией:
-
EventType
—CARD_CLICKED
. -
DialogEventType
—SUBMIT_DIALOG
.
Данные, которые пользователи вводят в диалоговое окно, доступны в событии взаимодействия как Event.common.formInputs
, карта, где ключи представляют собой строковые идентификаторы, назначенные каждому виджету диалогового окна, а значения представляют собой ввод пользователя для каждого виджета. Разные объекты представляют разные типы входных данных. Например, Event.common.formInputs.stringInputs
представляет строковые входные данные.
Когда пользователь отправляет диалог, ваше приложение Chat получает от Chat такое событие взаимодействия:
JSON
{
"type": enum (EventType),
"eventTime": string,
"threadKey": string,
"message": {
object (Message)
},
"user": {
object (User)
},
"space": {
object (Space)
},
"action": {
object (FormAction)
},
"configCompleteRedirectUrl": string,
// Indicates that this event is dialog-related.
"isDialogEvent": true,
// Indicates that a user clicked a button, and all data
// they entered in the dialog is included in Event.common.formInputs.
"dialogEventType": "SUBMIT_DIALOG",
"common": {
"userLocale": string,
"hostApp": enum (HostApp),
"platform": enum (Platform),
"timeZone": {
object (TimeZone)
},
// Represents user data entered in a dialog.
"formInputs": {
// Represents user data entered for a specific field in a dialog.
"NAME": {
// Represents string data entered in a dialog, like text input fields
// and check boxes.
"stringInputs": {
// An array of strings entered by the user in a dialog.
"value": [
string
]
}
}
},
"parameters": {
string: string,
...
},
"invokedFunction": string
}
}
Ваше приложение Chat может получить доступ к первому введенному пользователем значению в event.common.formInputs. NAME .stringInputs.value[0]
, где NAME — это поле name
виджета TextInput
.
После получения данных диалоговой формы приложение Chat должно ответить ActionResponse
:
- Чтобы подтвердить успешное получение, ответьте
ActionResponse
со значением"actionStatus": "OK"
. Это закроет диалог без публикации ответа. - Чтобы ответить текстовым сообщением или сообщением с карточкой, ответьте
ActionResponse
сResponseType
NEW_MESSAGE
,UPDATE_MESSAGE
илиUPDATE_USER_MESSAGE_CARDS
. Дополнительную информацию см. в разделе «Ответ на диалог» . - Чтобы вернуть ошибку, ответьте
ActionResponse
со значением"actionStatus": " ERROR MESSAGE "
.
В следующем примере проверяется наличие значения name
. Если он отсутствует, приложение «Чат» возвращает ошибку. Если оно присутствует, приложение «Чат» подтверждает получение данных формы и закрывает диалоговое окно.
Node.js
/**
* 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. Any "actionStatus" value other than "OK"
// gets returned as an error.
if (event.common.formInputs.WIDGET_NAME.stringInputs.value[0] === "") {
res.json({
"actionResponse": {
"type": "DIALOG",
"dialogAction": {
"actionStatus": "Don't forget to name your new contact!"
}
}
});
// Otherwise the Chat app indicates that it received
// form data from the dialog. An "actionStatus" of "OK" is
// interpreted as code 200, and the dialog closes.
} else {
res.json({
"actionResponse": {
"type": "DIALOG",
"dialogAction": {
"actionStatus": "OK"
}
}
});
}
}
Скрипт приложений
В этом примере отправляется карточное сообщение, возвращая card JSON . Вы также можете использовать службу карточек Apps Script .
/**
* 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. Any "actionStatus" value other than "OK"
// gets returned as an error.
if (event.common.formInputs.WIDGET_NAME[""].stringInputs.value[0] === "") {
return {
"actionResponse": {
"type": "DIALOG",
"dialogAction": {
"actionStatus": "Don't forget to name your new contact!"
}
}
};
// Otherwise the Chat app indicates that it received
// form data from the dialog. An "actionStatus" of "OK" is
// interpreted as code 200, and the dialog closes.
} else {
return {
"actionResponse": {
"type": "DIALOG",
"dialogAction": {
"actionStatus": "OK"
}
}
};
}
}
Питон
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 common := event.get('common'):
if form_inputs := common.get('formInputs'):
if contact_name := form_inputs.get('WIDGET_NAME'):
if string_inputs := contact_name.get('stringInputs'):
if name := string_inputs.get('value')[0]:
return {
'actionResponse': {
'type': 'DIALOG',
'dialogAction': {
'actionStatus': 'OK'
}
}
}
else:
return {
'actionResponse': {
'type': 'DIALOG',
'dialogAction': {
'actionStatus': 'Don\'t forget to name your new contact!'
}
}
}
Ответить на диалог
Вы можете ответить на диалог новым сообщением или обновлением существующего сообщения.
Ответить на диалог новым сообщением
Чтобы ответить на отправку диалоговой формы новым сообщением, приложение Chat возвращает ActionResponse
типа NEW_MESSAGE
вместе с разметкой , указывающей содержимое нового сообщения. После получения этого ответа диалоговое окно закрывается и публикуется новое сообщение.
Следующий код представляет собой пример ответа JSON на диалоговое окно, отправленное приложением чата для создания нового ответного сообщения:
JSON
{
"actionResponse": {
"type": "NEW_MESSAGE",
},
"text": "This message is a reply to a dialog form submission.",
"cardsV2": [
{
"cardId": "reply-card-id",
"card": {
"header": {
"title": "Reply card title"
},
"sections": [
{
"widgets": [
{
"textParagraph": {
"text": "Reply card message"
}
}
]
}
]
}
}
]
}
Приложение «Чат» также может асинхронно отвечать текстовым сообщением или карточкой .
Ответить на диалог обновленным сообщением
Когда вы отвечаете на диалог обновленным сообщением, вы можете обновить существующее сообщение приложения Chat или обновить предварительный просмотр ссылки.
Сообщения приложения чата
Чтобы ответить на отправку диалоговой формы обновлением существующего сообщения, отправленного приложением Chat, оно возвращает ActionResponse
типа UPDATE_MESSAGE
. Ответ включает разметку , указывающую содержимое обновленного сообщения. После получения этого ответа диалоговое окно закрывается, а сообщение обновляется новым содержимым.
Следующий код представляет собой пример ответа JSON на диалоговое окно, отправленное приложением чата для обновления существующего сообщения приложения чата:
JSON
{
"actionResponse": {
"type": "UPDATE_MESSAGE",
},
"text": "This message has been updated with new content in response to a dialog form submission.",
"cardsV2": [
{
"cardId": "updated-card-id",
"card": {
"header": {
"title": "Updated card title"
},
"sections": [
{
"widgets": [
{
"textParagraph": {
"text": "Updated card message"
}
}
]
}
]
}
}
]
}
Приложение Chat также может асинхронно обновлять сообщение приложения Chat с помощью API Google Chat.
Предварительный просмотр ссылок
Чтобы обновить предварительный просмотр ссылок новым содержимым в ответ на отправку диалоговой формы, приложение Chat возвращает ActionResponse
типа UPDATE_USER_MESSAGE_CARDS
. Ответ включает разметку для новых карточных сообщений, которые обновляются в предварительном просмотре ссылок. После получения этого ответа диалоговое окно закрывается, а предварительный просмотр ссылок обновляется новыми сообщениями карточек.
Следующий пример ответа JSON обновляет предварительный просмотр ссылки новым сообщением карточки:
JSON
{
"actionResponse": "UPDATE_USER_MESSAGE_CARDS",
"cardsV2": [
{
"cardId" : "updated-card-id",
"card" : {
"header": {
"title": "Updated card title"
},
"sections": [
{
"widgets" : [
{
"textParagraph": {
"text": "Updated card message"
}
}
]
}
]
}
}
]
}
Ответьте на диалоговое окно, которое появляется на главной странице приложения.
Только для главной страницы приложения существует два разных способа закрытия диалогового окна:
-
CLOSE_DIALOG
: закрывает диалоговое окно и возвращает на домашнюю карточку приложения. CLOSE_DIALOG_AND_EXECUTE
: закрывает диалоговое окно и обновляет домашнюю карточку приложения.
Питон
В следующем примере кода CLOSE_DIALOG
используется для закрытия диалогового окна и возврата к домашней карточке приложения:
def close_dialog():
"""Handles dismiss dialog request from Chat."""
return {
'render_actions': {
'action': {
'navigations': [{
'end_navigation': {'action': 'CLOSE_DIALOG'}
}]
}
}
}
Скрипт приложений
В этом примере отправляется карточное сообщение, возвращая card JSON . Вы также можете использовать сервис карточек Apps Script .
В следующем примере кода CLOSE_DIALOG
используется для закрытия диалогового окна и возврата к домашней карточке приложения:
function closeDialog(event) {
return {
render_actions: {
action:{
navigations:[{
end_navigation:{
action: "CLOSE_DIALOG"
}
}]
}
}
};
}
Полный пример: Rolodex — приложение чата для управления контактами.
В этом примере приложение чата открывает диалоговое окно, в котором пользователь может добавить сведения о контакте, такие как имя, адрес электронной почты и адрес:
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
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") {
openDialog(event);
}
if (event.common.invokedFunction === "openSequentialDialog") {
openSequentialDialog(event);
}
if (event.common.invokedFunction === "confirmDialogSuccess") {
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) {
res.json({
"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) {
res.json({
"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. Any "actionStatus" value other than "OK"
// gets returned as an error.
if (event.common.formInputs.contactName.stringInputs.value[0] === "") {
res.json({
"actionResponse": {
"type": "DIALOG",
"dialogAction": {
"actionStatus": "Don't forget to name your new contact!"
}
}
});
// Otherwise the Chat app indicates that it received
// form data from the dialog. An "actionStatus" of "OK" is
// interpreted as code 200, and the dialog closes.
} else {
res.json({
"actionResponse": {
"type": "DIALOG",
"dialogAction": {
"actionStatus": "OK"
}
}
});
}
}
Скрипт приложений
В этом примере отправляется карточное сообщение, возвращая card JSON . Вы также можете использовать сервис карточек Apps Script .
Питон
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!"
}
}
}
Устранение неполадок
Когда приложение или карточка Google Chat возвращает ошибку, в интерфейсе Chat отображается сообщение «Что-то пошло не так». или «Невозможно обработать ваш запрос». Иногда в пользовательском интерфейсе чата не отображается сообщение об ошибке, но приложение или карточка чата выдает неожиданный результат; например, сообщение с карточкой может не появиться.
Хотя сообщение об ошибке может не отображаться в пользовательском интерфейсе чата, доступны описательные сообщения об ошибках и данные журнала, которые помогут вам исправить ошибки, если включено ведение журнала ошибок для приложений чата. Информацию о просмотре, отладке и исправлении ошибок см. в разделе «Устранение неполадок и исправление ошибок Google Chat» .
Связанные темы
- Просмотрите примеры приложений Chat , использующих диалоговые окна
- Предварительный просмотр ссылок
- Настройка и ответ на слэш-команды
- Информация о процессе, введенная пользователями