1. Introduzione
Le app di Google Chat portano i tuoi servizi e le tue risorse direttamente in Google Chat, consentendo agli utenti di ottenere informazioni e intraprendere azioni rapide senza abbandonare la conversazione.
In questo codelab, imparerai a creare ed eseguire il deployment di un'app per sondaggi utilizzando Node.js e Cloud Functions.
Obiettivi didattici
- Utilizzare Cloud Shell
- Esegui il deployment in Cloud Functions
- Ottenere l'input dell'utente con i comandi slash e le finestre di dialogo
- Creare schede interattive
2. Configurazione e requisiti
Crea un progetto Google Cloud, quindi abilita le API e i servizi che verranno utilizzati dall'app Chat
Prerequisiti
Lo sviluppo di un'app Google Chat richiede un account Google Workspace con accesso a Google Chat. Se non hai ancora un account Google Workspace, creane uno e accedi prima di procedere con questo codelab.
Configurazione dell'ambiente autonomo
- Apri la console Google Cloud e crea un progetto.
Ricorda l'ID progetto, un nome univoco per tutti i progetti Google Cloud (il nome riportato sopra è già stato utilizzato e non funzionerà per te, ci dispiace). In questo codelab verrà chiamatoPROJECT_ID
.
- Successivamente, per utilizzare le risorse Google Cloud, abilita la fatturazione in Cloud Console.
L'esecuzione di questo codelab non dovrebbe costare molto, se non nulla. Assicurati di seguire le istruzioni nella sezione "Pulizia" alla fine del codelab, che ti consiglia come chiudere le risorse in modo da non incorrere in addebiti oltre questo tutorial. I nuovi utenti di Google Cloud possono beneficiare del programma prova senza costi di 300$.
Google Cloud Shell
Anche se Google Cloud può essere gestito da remoto dal tuo laptop, in questo codelab utilizzeremo Google Cloud Shell, un ambiente a riga di comando in esecuzione in Google Cloud.
Attiva Cloud Shell
- Nella console Google Cloud, fai clic su Attiva Cloud Shell
.
La prima volta che apri Cloud Shell, viene visualizzato un messaggio di benvenuto descrittivo. Se viene visualizzato il messaggio di benvenuto, fai clic su Continua. Il messaggio di benvenuto non viene più visualizzato. Ecco il messaggio di benvenuto:
Bastano pochi istanti per eseguire il provisioning e connettersi a Cloud Shell. Una volta effettuata la connessione, viene visualizzato il terminale Cloud Shell:
Questa macchina virtuale è caricata con tutti gli strumenti di sviluppo necessari. Offre una home directory permanente da 5 GB e viene eseguita in Google Cloud, migliorando notevolmente le prestazioni di rete e l'autenticazione. Tutto il lavoro in questo codelab può essere svolto con un browser o Chromebook.Una volta eseguita la connessione a Cloud Shell, dovresti vedere che il tuo account è già autenticato e il progetto è già impostato sul tuo ID progetto. - Esegui questo comando in Cloud Shell per verificare che l'account sia autenticato:
Se ti viene chiesto di autorizzare Cloud Shell a effettuare una chiamata API Google Cloud, fai clic su Autorizza.gcloud auth list
Output del comando Se il tuo account non è selezionato per impostazione predefinita, esegui:Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com>
$ gcloud config set account <ACCOUNT>
- Conferma di aver selezionato il progetto corretto. In Cloud Shell, esegui:
Output comandogcloud config list project
Se non viene restituito il progetto corretto, puoi impostarlo con questo comando:[core] project = <PROJECT_ID>
Output comandogcloud config set project <PROJECT_ID>
Updated property [core/project].
Man mano che completi questo codelab, utilizzerai le operazioni della riga di comando e modificherai i file. Per la modifica dei file, puoi utilizzare l'editor di codice integrato di Cloud Shell, Cloud Shell Editor, facendo clic su Apri editor sul lato destro della barra degli strumenti di Cloud Shell. In Cloud Shell sono disponibili anche editor popolari come Vim ed Emacs.
3. Abilita le API Cloud Functions, Cloud Build e Google Chat
Da Cloud Shell, abilita queste API e questi servizi:
gcloud services enable \ cloudfunctions \ cloudbuild.googleapis.com \ chat.googleapis.com
Il completamento di questa operazione potrebbe richiedere alcuni istanti.
Al termine, viene visualizzato un messaggio di operazione riuscita simile a questo:
Operation "operations/acf.cc11852d-40af-47ad-9d59-477a12847c9e" finished successfully.
4. Creare l'app Chat iniziale
Inizializza il progetto
Per iniziare, creerai e implementerai una semplice app "Hello world". Le app di chat sono servizi web che rispondono alle richieste https e restituiscono un payload JSON. Per questa app, utilizzerai Node.js e Cloud Functions.
In Cloud Shell, crea una nuova directory denominata poll-app
e vai alla directory:
mkdir ~/poll-app cd ~/poll-app
Tutto il lavoro rimanente per il codelab e i file che creerai si troveranno in questa directory.
Inizializza il progetto Node.js:
npm init
NPM pone diverse domande sulla configurazione del progetto, ad esempio nome e versione. Per ogni domanda, premi ENTER
per accettare i valori predefiniti. Il punto di ingresso predefinito è un file denominato index.js
, che creeremo in seguito.
Crea il backend dell'app di chat
È ora di iniziare a creare l'app. Crea un file denominato index.js
con i seguenti contenuti:
/**
* App entry point.
*/
exports.app = async (req, res) => {
if (!(req.method === 'POST' && req.body)) {
res.status(400).send('')
}
const event = req.body;
let reply = {};
if (event.type === 'MESSAGE') {
reply = {
text: `Hello ${event.user.displayName}`
};
}
res.json(reply)
}
L'app non farà ancora molto, ma va bene così. Aggiungerai altre funzionalità in un secondo momento.
Esegui il deployment dell'app
Per eseguire il deployment dell'app "Hello world", devi eseguire il deployment della funzione Cloud, configurare l'app di chat nella console Google Cloud e inviare un messaggio di test all'app per verificare il deployment.
Esegui il deployment della funzione Cloud Functions
Per eseguire il deployment della funzione Cloud Functions dell'app"Hello World", inserisci il seguente comando:
gcloud functions deploy app --trigger-http --security-level=secure-always --allow-unauthenticated --runtime nodejs14
Al termine, l'output dovrebbe essere simile al seguente:
availableMemoryMb: 256
buildId: 993b2ca9-2719-40af-86e4-42c8e4563a4b
buildName: projects/595241540133/locations/us-central1/builds/993b2ca9-2719-40af-86e4-42c8e4563a4b
entryPoint: app
httpsTrigger:
securityLevel: SECURE_ALWAYS
url: https://us-central1-poll-app-codelab.cloudfunctions.net/app
ingressSettings: ALLOW_ALL
labels:
deployment-tool: cli-gcloud
name: projects/poll-app-codelab/locations/us-central1/functions/app
runtime: nodejs14
serviceAccountEmail: poll-app-codelab@appspot.gserviceaccount.com
sourceUploadUrl: https://storage.googleapis.com/gcf-upload-us-central1-66a01777-67f0-46d7-a941-079c24414822/94057943-2b7c-4b4c-9a21-bb3acffc84c6.zip
status: ACTIVE
timeout: 60s
updateTime: '2021-09-17T19:30:33.694Z'
versionId: '1'
Prendi nota dell'URL della funzione di cui è stato eseguito il deployment nella proprietà httpsTrigger.url
. Lo utilizzerai nel passaggio successivo.
Configurare l'app
Per configurare l'app, vai alla pagina Configurazione della chat in Cloud Console.
- Deseleziona Crea questa app di Chat come componente aggiuntivo di Workspace e fai clic su DISATTIVA per confermare.
- In Nome app, inserisci "PollCodelab".
- In URL avatar, inserisci
https://raw.githubusercontent.com/google/material-design-icons/master/png/social/poll/materialicons/24dp/2x/baseline_poll_black_24dp.png
. - In Descrizione, inserisci "App per sondaggi per codelab".
- Nella sezione Funzionalità, seleziona Ricevi messaggi 1:1 e Partecipa a spazi e conversazioni di gruppo.
- Nella sezione Impostazioni di connessione, seleziona URL endpoint HTTP e incolla l'URL della funzione Cloud (la proprietà
httpsTrigger.url
dell'ultima sezione). - Nella sezione Autorizzazioni, seleziona Persone e gruppi specifici nel tuo dominio e inserisci il tuo indirizzo email.
- Fai clic su Salva.
L'app è ora pronta per inviare messaggi.
Testare l'app
Prima di procedere, verifica che l'app funzioni aggiungendola a uno spazio in Google Chat.
- Vai a Google Chat.
- Accanto a Chat, fai clic su + > Trova app.
- Inserisci "PollCodelab" nella ricerca.
- Fai clic su Chat.
- Per inviare un messaggio all'app, digita "Ciao" e premi Invio.
L'app dovrebbe rispondere con un breve messaggio di saluto.
Ora che abbiamo una struttura di base, è il momento di trasformarla in qualcosa di più utile.
5. Creare le funzionalità del sondaggio
Una rapida panoramica del funzionamento dell'app
L'app è costituita da due parti principali:
- Un comando slash che mostra una finestra di dialogo per configurare il sondaggio.
- Una scheda interattiva per votare e visualizzare i risultati.
L'app ha anche bisogno di uno stato per memorizzare la configurazione e i risultati del sondaggio. Questa operazione può essere eseguita con Firestore o qualsiasi database oppure lo stato può essere memorizzato nei messaggi dell'app. Poiché questa app è pensata per sondaggi informali rapidi di un team, memorizzare lo stato nei messaggi dell'app è perfetto per questo caso d'uso.
Il modello di dati per l'app (espresso in TypeScript) è:
interface Poll {
/* Question/topic of poll */
topic: string;
/** User that submitted the poll */
author: {
/** Unique resource name of user */
name: string;
/** Display name */
displayName: string;
};
/** Available choices to present to users */
choices: string[];
/** Map of user ids to the index of their selected choice */
votes: { [key: string]: number };
}
Oltre all'argomento o alla domanda e a un elenco di scelte, lo stato include l'ID e il nome dell'autore, nonché i voti registrati. Per impedire agli utenti di votare più volte, i voti vengono memorizzati come una mappa degli ID utente all'indice della scelta selezionata.
Esistono, ovviamente, molti approcci diversi, ma questo fornisce un buon punto di partenza per eseguire sondaggi rapidi in uno spazio.
Implementa il comando di configurazione del sondaggio
Per consentire agli utenti di avviare e configurare i sondaggi, configura un comando slash che apre una finestra di dialogo. Si tratta di una procedura in più passaggi:
- Registra il comando slash che avvia un sondaggio.
- Crea la finestra di dialogo per impostare un sondaggio.
- Consenti all'app di riconoscere e gestire il comando slash.
- Crea schede interattive che facilitano la votazione nel sondaggio.
- Implementa il codice che consente all'app di eseguire sondaggi.
- Esegui nuovamente il deployment della funzione Cloud Functions.
Registra il comando slash
Per registrare un comando slash, torna alla pagina Configurazione chat nella console (API e servizi > Dashboard > API Hangouts Chat > Configurazione).
- In Comandi slash, fai clic su Aggiungi un nuovo comando slash.
- In Nome, inserisci "/poll".
- In ID comando, inserisci "1".
- In Descrizione, inserisci "Avvia un sondaggio".
- Seleziona Apre una finestra di dialogo.
- Fai clic su Fine.
- Fai clic su Salva.
L'app ora riconosce il comando /poll
e apre una finestra di dialogo. Ora configuriamo la finestra di dialogo.
Crea il modulo di configurazione come finestra di dialogo
Il comando slash apre una finestra di dialogo per configurare l'argomento del sondaggio e le possibili scelte. Crea un nuovo file denominato config-form.js
con i seguenti contenuti:
/** Upper bounds on number of choices to present. */
const MAX_NUM_OF_OPTIONS = 5;
/**
* Build widget with instructions on how to use form.
*
* @returns {object} card widget
*/
function helpText() {
return {
textParagraph: {
text: 'Enter the poll topic and up to 5 choices in the poll. Blank options will be omitted.',
},
};
}
/**
* Build the text input for a choice.
*
* @param {number} index - Index to identify the choice
* @param {string|undefined} value - Initial value to render (optional)
* @returns {object} card widget
*/
function optionInput(index, value) {
return {
textInput: {
label: `Option ${index + 1}`,
type: 'SINGLE_LINE',
name: `option${index}`,
value: value || '',
},
};
}
/**
* Build the text input for the poll topic.
*
* @param {string|undefined} topic - Initial value to render (optional)
* @returns {object} card widget
*/
function topicInput(topic) {
return {
textInput: {
label: 'Topic',
type: 'MULTIPLE_LINE',
name: 'topic',
value: topic || '',
},
};
}
/**
* Build the buttons/actions for the form.
*
* @returns {object} card widget
*/
function buttons() {
return {
buttonList: {
buttons: [
{
text: 'Submit',
onClick: {
action: {
function: 'start_poll',
},
},
},
],
},
};
}
/**
* Build the configuration form.
*
* @param {object} options - Initial state to render with form
* @param {string|undefined} options.topic - Topic of poll (optional)
* @param {string[]|undefined} options.choices - Text of choices to display to users (optional)
* @returns {object} card
*/
function buildConfigurationForm(options) {
const widgets = [];
widgets.push(helpText());
widgets.push(topicInput(options.topic));
for (let i = 0; i < MAX_NUM_OF_OPTIONS; ++i) {
const choice = options?.choices?.[i];
widgets.push(optionInput(i, choice));
}
widgets.push(buttons());
// Assemble the card
return {
sections: [
{
widgets,
},
],
};
}
exports.MAX_NUM_OF_OPTIONS = MAX_NUM_OF_OPTIONS;
exports.buildConfigurationForm = buildConfigurationForm;
Questo codice genera il modulo di dialogo che consente all'utente di configurare il sondaggio. Esporta anche una costante per il numero massimo di scelte che una domanda può avere. È buona pratica isolare la creazione del markup dell'interfaccia utente in funzioni stateless con qualsiasi stato passato come parametri. Facilita il riutilizzo e in un secondo momento questa scheda verrà visualizzata in contesti diversi.
Questa implementazione scompone anche la scheda in unità o componenti più piccoli. Sebbene non sia obbligatoria, questa tecnica è una best practice perché tende a essere più leggibile e gestibile quando si creano interfacce complesse.
Per visualizzare un esempio del JSON completo che crea, visualizzalo nello strumento Card Builder.
Gestire il comando slash
I comandi slash vengono visualizzati come eventi MESSAGE
quando vengono inviati all'app. Aggiorna index.js
per verificare la presenza di un comando slash tramite un evento MESSAGE
e per rispondere con una finestra di dialogo. Sostituisci index.js
con quanto segue:
const { buildConfigurationForm, MAX_NUM_OF_OPTIONS } = require('./config-form');
/**
* App entry point.
*/
exports.app = async (req, res) => {
if (!(req.method === 'POST' && req.body)) {
res.status(400).send('')
}
const event = req.body;
let reply = {};
// Dispatch slash and action events
if (event.type === 'MESSAGE') {
const message = event.message;
if (message.slashCommand?.commandId === '1') {
reply = showConfigurationForm(event);
}
} else if (event.type === 'CARD_CLICKED') {
if (event.action?.actionMethodName === 'start_poll') {
reply = await startPoll(event);
}
}
res.json(reply);
}
/**
* Handles the slash command to display the config form.
*
* @param {object} event - chat event
* @returns {object} Response to send back to Chat
*/
function showConfigurationForm(event) {
// Seed the topic with any text after the slash command
const topic = event.message?.argumentText?.trim();
const dialog = buildConfigurationForm({
topic,
choices: [],
});
return {
actionResponse: {
type: 'DIALOG',
dialogAction: {
dialog: {
body: dialog,
},
},
},
};
}
/**
* Handle the custom start_poll action.
*
* @param {object} event - chat event
* @returns {object} Response to send back to Chat
*/
function startPoll(event) {
// Not fully implemented yet -- just close the dialog
return {
actionResponse: {
type: 'DIALOG',
dialogAction: {
actionStatus: {
statusCode: 'OK',
userFacingMessage: 'Poll started.',
},
},
},
}
}
L'app ora mostrerà una finestra di dialogo con la data e l'ora in cui viene richiamato il comando /poll
. Testa l'interazione eseguendo nuovamente il deployment della funzione Cloud da Cloud Shell.
gcloud functions deploy app --trigger-http --security-level=secure-always
Dopo il deployment della funzione Cloud Functions, invia un messaggio all'app con il comando /poll
per testare il comando slash e la finestra di dialogo. La finestra di dialogo invia un CARD_CLICKED
evento con l'azione personalizzata start_poll
. L'evento viene gestito nel punto di ingresso aggiornato in cui chiama il metodo startPoll
. Per il momento, il metodo startPoll
è stato implementato solo per chiudere la finestra di dialogo. Nella sezione successiva, implementerai la funzionalità di voto e collegherai tutte le parti.
Implementare la scheda di voto
Per implementare la parte di voto dell'app, inizia definendo la scheda interattiva che fornisce un'interfaccia per consentire alle persone di votare.
Implementare l'interfaccia di voto
Crea un file denominato vote-card.js
con i seguenti contenuti:
/**
* Creates a small progress bar to show percent of votes for an option. Since
* width is limited, the percentage is scaled to 20 steps (5% increments).
*
* @param {number} voteCount - Number of votes for this option
* @param {number} totalVotes - Total votes cast in the poll
* @returns {string} Text snippet with bar and vote totals
*/
function progressBarText(voteCount, totalVotes) {
if (voteCount === 0 || totalVotes === 0) {
return '';
}
// For progress bar, calculate share of votes and scale it
const percentage = (voteCount * 100) / totalVotes;
const progress = Math.round((percentage / 100) * 20);
return '▀'.repeat(progress);
}
/**
* Builds a line in the card for a single choice, including
* the current totals and voting action.
*
* @param {number} index - Index to identify the choice
* @param {string|undefined} value - Text of the choice
* @param {number} voteCount - Current number of votes cast for this item
* @param {number} totalVotes - Total votes cast in poll
* @param {string} state - Serialized state to send in events
* @returns {object} card widget
*/
function choice(index, text, voteCount, totalVotes, state) {
const progressBar = progressBarText(voteCount, totalVotes);
return {
keyValue: {
bottomLabel: `${progressBar} ${voteCount}`,
content: text,
button: {
textButton: {
text: 'vote',
onClick: {
action: {
actionMethodName: 'vote',
parameters: [
{
key: 'state',
value: state,
},
{
key: 'index',
value: index.toString(10),
},
],
},
},
},
},
},
};
}
/**
* Builds the card header including the question and author details.
*
* @param {string} topic - Topic of the poll
* @param {string} author - Display name of user that created the poll
* @returns {object} card widget
*/
function header(topic, author) {
return {
title: topic,
subtitle: `Posted by ${author}`,
imageUrl:
'https://raw.githubusercontent.com/google/material-design-icons/master/png/social/poll/materialicons/24dp/2x/baseline_poll_black_24dp.png',
imageStyle: 'AVATAR',
};
}
/**
* Builds the configuration form.
*
* @param {object} poll - Current state of poll
* @param {object} poll.author - User that submitted the poll
* @param {string} poll.topic - Topic of poll
* @param {string[]} poll.choices - Text of choices to display to users
* @param {object} poll.votes - Map of cast votes keyed by user ids
* @returns {object} card
*/
function buildVoteCard(poll) {
const widgets = [];
const state = JSON.stringify(poll);
const totalVotes = Object.keys(poll.votes).length;
for (let i = 0; i < poll.choices.length; ++i) {
// Count votes for this choice
const votes = Object.values(poll.votes).reduce((sum, vote) => {
if (vote === i) {
return sum + 1;
}
return sum;
}, 0);
widgets.push(choice(i, poll.choices[i], votes, totalVotes, state));
}
return {
header: header(poll.topic, poll.author.displayName),
sections: [
{
widgets,
},
],
};
}
exports.buildVoteCard = buildVoteCard;
L'implementazione è simile all'approccio adottato con la finestra di dialogo, anche se il markup per le schede interattive è leggermente diverso da quello delle finestre di dialogo. Come in precedenza, puoi visualizzare un campione del JSON generato nello strumento Card Builder.
Implementa l'azione di voto
La scheda di voto include un pulsante per ogni opzione. L'indice di questa scelta, insieme allo stato serializzato del sondaggio, è allegato al pulsante. L'app riceve un CARD_CLICKED
con l'azione vote
insieme a tutti i dati allegati al pulsante come parametri.
Aggiorna index.js
con:
const { buildConfigurationForm, MAX_NUM_OF_OPTIONS } = require('./config-form');
const { buildVoteCard } = require('./vote-card');
/**
* App entry point.
*/
exports.app = async (req, res) => {
if (!(req.method === 'POST' && req.body)) {
res.status(400).send('')
}
const event = req.body;
let reply = {};
// Dispatch slash and action events
if (event.type === 'MESSAGE') {
const message = event.message;
if (message.slashCommand?.commandId === '1') {
reply = showConfigurationForm(event);
}
} else if (event.type === 'CARD_CLICKED') {
if (event.action?.actionMethodName === 'start_poll') {
reply = await startPoll(event);
} else if (event.action?.actionMethodName === 'vote') {
reply = recordVote(event);
}
}
res.json(reply);
}
/**
* Handles the slash command to display the config form.
*
* @param {object} event - chat event
* @returns {object} Response to send back to Chat
*/
function showConfigurationForm(event) {
// Seed the topic with any text after the slash command
const topic = event.message?.argumentText?.trim();
const dialog = buildConfigurationForm({
topic,
choices: [],
});
return {
actionResponse: {
type: 'DIALOG',
dialogAction: {
dialog: {
body: dialog,
},
},
},
};
}
/**
* Handle the custom start_poll action.
*
* @param {object} event - chat event
* @returns {object} Response to send back to Chat
*/
function startPoll(event) {
// Not fully implemented yet -- just close the dialog
return {
actionResponse: {
type: 'DIALOG',
dialogAction: {
actionStatus: {
statusCode: 'OK',
userFacingMessage: 'Poll started.',
},
},
},
}
}
/**
* Handle the custom vote action. Updates the state to record
* the user's vote then rerenders the card.
*
* @param {object} event - chat event
* @returns {object} Response to send back to Chat
*/
function recordVote(event) {
const parameters = event.common?.parameters;
const choice = parseInt(parameters['index']);
const userId = event.user.name;
const state = JSON.parse(parameters['state']);
// Add or update the user's selected option
state.votes[userId] = choice;
const card = buildVoteCard(state);
return {
thread: event.message.thread,
actionResponse: {
type: 'UPDATE_MESSAGE',
},
cards: [card],
}
}
Il metodo recordVote
analizza lo stato memorizzato e lo aggiorna con il voto dell'utente, quindi esegue nuovamente il rendering della scheda. I risultati del sondaggio vengono serializzati e memorizzati con la scheda ogni volta che viene aggiornata.
Collegare i pezzi
L'app è quasi pronta. Con l'implementazione del comando con barra insieme al voto, l'unica cosa rimasta da fare è completare il metodo startPoll
.
Ma c'è un problema.
Quando viene inviata la configurazione del sondaggio, l'app deve eseguire due azioni:
- Chiudi la finestra di dialogo.
- Pubblica un nuovo messaggio nello spazio con la scheda di voto.
Purtroppo, la risposta diretta alla richiesta HTTP può essere solo una e deve essere la prima. Per pubblicare la scheda di voto, l'app deve utilizzare l'API Chat per creare un nuovo messaggio in modo asincrono.
Aggiungere la libreria client
Esegui questo comando per aggiornare le dipendenze dell'app in modo da includere il client API Google per Node.js.
npm install --save googleapis
Avvia il sondaggio
Aggiorna index.js
alla versione finale riportata di seguito:
const { buildConfigurationForm, MAX_NUM_OF_OPTIONS } = require('./config-form');
const { buildVoteCard } = require('./vote-card');
const {google} = require('googleapis');
/**
* App entry point.
*/
exports.app = async (req, res) => {
if (!(req.method === 'POST' && req.body)) {
res.status(400).send('')
}
const event = req.body;
let reply = {};
// Dispatch slash and action events
if (event.type === 'MESSAGE') {
const message = event.message;
if (message.slashCommand?.commandId === '1') {
reply = showConfigurationForm(event);
}
} else if (event.type === 'CARD_CLICKED') {
if (event.action?.actionMethodName === 'start_poll') {
reply = await startPoll(event);
} else if (event.action?.actionMethodName === 'vote') {
reply = recordVote(event);
}
}
res.json(reply);
}
/**
* Handles the slash command to display the config form.
*
* @param {object} event - chat event
* @returns {object} Response to send back to Chat
*/
function showConfigurationForm(event) {
// Seed the topic with any text after the slash command
const topic = event.message?.argumentText?.trim();
const dialog = buildConfigurationForm({
topic,
choices: [],
});
return {
actionResponse: {
type: 'DIALOG',
dialogAction: {
dialog: {
body: dialog,
},
},
},
};
}
/**
* Handle the custom start_poll action.
*
* @param {object} event - chat event
* @returns {object} Response to send back to Chat
*/
async function startPoll(event) {
// Get the form values
const formValues = event.common?.formInputs;
const topic = formValues?.['topic']?.stringInputs.value[0]?.trim();
const choices = [];
for (let i = 0; i < MAX_NUM_OF_OPTIONS; ++i) {
const choice = formValues?.[`option${i}`]?.stringInputs.value[0]?.trim();
if (choice) {
choices.push(choice);
}
}
if (!topic || choices.length === 0) {
// Incomplete form submitted, rerender
const dialog = buildConfigurationForm({
topic,
choices,
});
return {
actionResponse: {
type: 'DIALOG',
dialogAction: {
dialog: {
body: dialog,
},
},
},
};
}
// Valid configuration, build the voting card to display
// in the space
const pollCard = buildVoteCard({
topic: topic,
author: event.user,
choices: choices,
votes: {},
});
const message = {
cards: [pollCard],
};
const request = {
parent: event.space.name,
requestBody: message,
};
// Use default credentials (service account)
const credentials = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/chat.bot'],
});
const chatApi = google.chat({
version: 'v1',
auth: credentials,
});
await chatApi.spaces.messages.create(request);
// Close dialog
return {
actionResponse: {
type: 'DIALOG',
dialogAction: {
actionStatus: {
statusCode: 'OK',
userFacingMessage: 'Poll started.',
},
},
},
};
}
/**
* Handle the custom vote action. Updates the state to record
* the user's vote then rerenders the card.
*
* @param {object} event - chat event
* @returns {object} Response to send back to Chat
*/
function recordVote(event) {
const parameters = event.common?.parameters;
const choice = parseInt(parameters['index']);
const userId = event.user.name;
const state = JSON.parse(parameters['state']);
// Add or update the user's selected option
state.votes[userId] = choice;
const card = buildVoteCard(state);
return {
thread: event.message.thread,
actionResponse: {
type: 'UPDATE_MESSAGE',
},
cards: [card],
}
}
Esegui nuovamente il deployment della funzione:
gcloud functions deploy app --trigger-http --security-level=secure-always
Ora dovresti essere in grado di utilizzare completamente l'app. Prova a richiamare il comando /poll
, fornisci una domanda e alcune opzioni. Dopo l'invio, viene visualizzata la scheda del sondaggio.
Vota e scopri cosa succede.
Ovviamente, votare da soli non è molto utile, quindi invita alcuni amici o colleghi a provare.
6. Complimenti
Complimenti! Hai creato e sottoposto a deployment un'app Google Chat utilizzando Cloud Functions. Il codelab ha trattato molti dei concetti di base per la creazione di un'app, ma c'è ancora molto da esplorare. Consulta le risorse di seguito e non dimenticare di eseguire la pulizia del progetto per evitare addebiti aggiuntivi.
Attività aggiuntive
Se vuoi esplorare più a fondo la piattaforma Chat e questa app, ecco alcune cose che puoi provare da solo:
- Cosa succede quando menzioni l'app con @? Prova ad aggiornare l'app per migliorare il comportamento.
- La serializzazione dello stato del sondaggio nella scheda è adatta a spazi ridotti, ma presenta dei limiti. Prova a passare a un'opzione migliore.
- Cosa succede se l'autore vuole modificare il sondaggio o interrompere la raccolta di nuovi voti? Come implementeresti queste funzionalità?
- L'endpoint dell'app non è ancora protetto. Prova ad aggiungere una verifica per assicurarti che le richieste provengano da Google Chat.
Questi sono solo alcuni modi diversi per migliorare l'app. Divertiti e usa la tua immaginazione.
Esegui la pulizia
Per evitare che al tuo account Google Cloud Platform vengano addebitati costi relativi alle risorse utilizzate in questo tutorial:
- In Cloud Console, vai alla pagina Gestisci risorse. In alto a sinistra, fai clic su Menu
> IAM e amministrazione > Gestisci risorse.
- Nell'elenco dei progetti, seleziona il tuo progetto e fai clic su Elimina.
- Nella finestra di dialogo, digita l'ID progetto, quindi fai clic su Chiudi per eliminare il progetto.
Scopri di più
Per maggiori informazioni sullo sviluppo di app di Chat, vedi:
Per saperne di più sullo sviluppo in Google Cloud Console, consulta: