1. Introduction
Les applications Google Chat fournissent des services et des ressources directement dans Google Chat, ce qui permet aux utilisateurs de chercher des informations et d'effectuer des actions rapides sans quitter la conversation.
Au cours de cet atelier de programmation, vous allez apprendre à créer et à déployer une application de sondage à l'aide de Node.js et de Cloud Functions.
Points abordés
- Utiliser Cloud Shell
- Déployer une application dans Cloud Functions
- Obtenir les entrées utilisateur à l'aide des commandes à barre oblique et des boîtes de dialogue
- Créer des fiches interactives
2. Préparation
Créez un projet Google Cloud, puis activez les API et les services que l'application Chat utilisera.
Prérequis
Pour développer une application Google Chat, vous devez disposer d'un compte Google Workspace ayant accès à Google Chat. Si vous n'avez pas encore de compte Google Workspace, créez-en un et connectez-vous avant de poursuivre cet atelier de programmation.
Configuration de l'environnement d'auto-formation
- Ouvrez la console Google Cloud et créez un projet.
Notez que l'ID du projet est un nom unique permettant de différencier chaque projet Google Cloud (le nom ci-dessus est déjà pris ; vous devez en trouver un autre). Il sera désigné par le nomPROJECT_ID
tout au long de cet atelier de programmation.
- Ensuite, pour pouvoir utiliser les ressources Google Cloud, activez la facturation dans la console Cloud.
L'exécution de cet atelier de programmation est très peu coûteuse, voire sans frais. Veillez à suivre les instructions de la section "Effectuer un nettoyage" à la fin de l'atelier de programmation, qui indique comment désactiver les ressources afin d'éviter toute facturation au-delà de ce tutoriel. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300 $.
Google Cloud Shell
Bien que Google Cloud puisse être utilisé à distance depuis votre ordinateur portable, nous allons nous servir de Google Cloud Shell pour cet atelier de programmation, un environnement de ligne de commande exécuté dans Google Cloud.
Activer Cloud Shell
- Dans la console Cloud, cliquez sur Activer Cloud Shell
.
Lorsque vous ouvrez Cloud Shell pour la première fois, un message de bienvenue descriptif s'affiche. Si le message de bienvenue apparaît, cliquez sur Continuer. Le message de bienvenue n'apparaîtra plus. Voici le message de bienvenue :
Le provisionnement et la connexion à Cloud Shell ne devraient pas prendre plus de quelques minutes. Une fois connecté, le terminal Cloud Shell s'affiche :
Cette machine virtuelle contient tous les outils de développement dont vous avez besoin. Elle comprend un répertoire d'accueil persistant de 5 Go et s'exécute sur Google Cloud, ce qui améliore nettement les performances du réseau et l'authentification. Vous pouvez réaliser la totalité des activités de cet atelier dans un simple navigateur ou sur votre Chromebook. Une fois connecté à Cloud Shell, vous êtes en principe authentifié, et le projet est déjà défini avec votre ID de projet. - Exécutez la commande suivante dans Cloud Shell pour vérifier que vous êtes authentifié :
Si vous êtes invité à autoriser Cloud Shell à effectuer un appel d'API GCP, cliquez sur Autoriser.gcloud auth list
Résultat de la commande Si votre compte n'est pas sélectionné par défaut, exécutez la commande suivante :Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com>
$ gcloud config set account <ACCOUNT>
- Assurez-vous d'avoir sélectionné le projet concerné. Dans Cloud Shell, exécutez la commande suivante :
Résultat de la commandegcloud config list project
Si vous obtenez un résultat différent, exécutez cette commande :[core] project = <PROJECT_ID>
Résultat de la commandegcloud config set project <PROJECT_ID>
Updated property [core/project].
Au cours de cet atelier de programmation, vous utiliserez des opérations de ligne de commande et modifierez des fichiers. Pour modifier des fichiers, vous pouvez utiliser l'éditeur de code intégré de Cloud Shell, l'éditeur Cloud Shell, en cliquant sur Ouvrir l'éditeur sur la droite de la barre d'outils Cloud Shell. D'autres éditeurs courants, tels que Vim et Emacs, sont également disponibles dans Cloud Shell.
3. Activer Cloud Functions, Cloud Build et les API Google Chat
Dans Cloud Shell, activez les API et services suivants :
gcloud services enable \ cloudfunctions \ cloudbuild.googleapis.com \ chat.googleapis.com
Cette opération peut prendre quelques minutes.
Une fois l'opération terminée, un message de confirmation semblable à celui-ci s'affiche :
Operation "operations/acf.cc11852d-40af-47ad-9d59-477a12847c9e" finished successfully.
4. Créer l'application Chat initiale
Initialiser le projet
Pour commencer, vous allez créer et déployer une application "Hello World" simple. Les applications Chat sont des services Web qui répondent aux requêtes HTTPS avec une charge utile JSON. Pour cette application, vous utiliserez Node.js et Cloud Functions.
Dans Cloud Shell, créez un répertoire nommé poll-app
et accédez-y :
mkdir ~/poll-app cd ~/poll-app
Tout le travail restant de cet atelier de programmation et les fichiers que vous allez créer seront stockés dans ce répertoire.
Initialisez le projet Node.js :
npm init
GPR pose plusieurs questions sur la configuration du projet, telles que son nom et sa version. Pour chaque question, appuyez sur ENTER
pour accepter les valeurs par défaut. Le point d'entrée par défaut est un fichier nommé index.js
, que nous allons créer ensuite.
Créer le backend de l'application Chat
Commençons à créer l'application. Créez un fichier nommé index.js
avec le contenu suivant :
/**
* 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)
}
Pour le moment, l'application ne fera que peu de choses, mais ce n'est pas grave. Vous ajouterez des fonctionnalités par la suite.
Déployer l'application
Pour déployer l'application "Hello World", vous devrez déployer la fonction Cloud, configurer l'application Chat dans la console Google Cloud, puis envoyer un message de test à l'application pour vérifier le déploiement.
Déployer la fonction Cloud
Pour déployer la fonction Cloud de l'application "Hello World", saisissez la commande suivante :
gcloud functions deploy app --trigger-http --security-level=secure-always --allow-unauthenticated --runtime nodejs14
Une fois l'opération terminée, le résultat devrait ressembler à ceci :
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'
Notez l'URL de la fonction déployée dans la propriété httpsTrigger.url
. Vous en aurez besoin à l'étape suivante.
Configurer l'application
Pour configurer l'application, accédez à la page de configuration de Chat dans la console Cloud.
- Décochez Créer cette application Chat en tant que module complémentaire Workspace, puis cliquez sur DÉSACTIVER pour confirmer.
- Dans Nom de l'application, saisissez "PollCodelab".
- Dans URL de l'avatar, saisissez
https://raw.githubusercontent.com/google/material-design-icons/master/png/social/poll/materialicons/24dp/2x/baseline_poll_black_24dp.png
. - Dans Description, saisissez "Application de sondage pour l'atelier de programmation".
- Sous Fonctionnalité, sélectionnez Recevoir des messages privés et Rejoindre des espaces et des conversations de groupe.
- Sous Paramètres de connexion, sélectionnez URL du point de terminaison HTTP et collez l'URL de la fonction Cloud (la propriété
httpsTrigger.url
utilisée à la section précédente). - Sous Autorisations, sélectionnez Utilisateurs et groupes spécifiques de votre domaine et saisissez votre adresse e-mail.
- Cliquez sur Enregistrer.
L'application est prête à envoyer des messages.
Tester l'application
Avant de poursuivre, vérifiez que l'application fonctionne en l'ajoutant à un espace dans Google Chat.
- Accédez à Google Chat.
- À côté de Chat, cliquez sur + > Rechercher des applications.
- Saisissez "PollCodelab" dans le champ de recherche.
- Cliquez sur Chat.
- Pour envoyer un message à l'application, tapez "bonjour" et appuyez sur Entrée.
L'application devrait répondre par un bref message de bienvenue.
Maintenant que nous avons un squelette de base, il est temps de le transformer en un objet plus utile.
5. Créer les fonctionnalités de sondage
Présentation rapide du fonctionnement de l'application
L'application se compose de deux éléments principaux :
- Une commande à barre oblique qui affiche une boîte de dialogue pour configurer le sondage.
- Une fiche interactive pour voter et afficher les résultats.
L'application requiert également un état pour stocker la configuration du sondage et les résultats. Pour ce faire, utilisez Firestore ou n'importe quelle base de données, ou l'état peut être stocké dans les messages de l'application eux-mêmes. Comme cette application est conçue pour réaliser des sondages rapides et informels d'une équipe, stocker l'état dans les messages de l'application est très efficace dans ce cas d'utilisation.
Le modèle de données de l'application (exprimé en TypeScript) est le suivant :
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 };
}
Outre le sujet ou la question, et la liste des choix, l'état inclut l'ID et le nom de l'auteur, ainsi que les votes enregistrés. Pour éviter que les utilisateurs ne votent plusieurs fois, les votes sont stockés de façon à faire correspondre les ID utilisateur à l'index du choix qu'ils ont sélectionné.
Il existe bien sûr de nombreuses approches différentes, mais cette méthode offre un bon point de départ pour exécuter des sondages rapides dans un espace.
Implémenter la commande de configuration de sondage
Pour permettre aux utilisateurs de démarrer et de configurer des sondages, configurez une commande à barre oblique qui ouvre une boîte de dialogue. Ce processus comporte plusieurs étapes :
- Enregistrez la commande à barre oblique qui lance un sondage.
- Créez la boîte de dialogue qui configure un sondage.
- Autorisez l'application à reconnaître et à gérer la commande à barre oblique.
- Créez des fiches interactives qui facilitent le vote dans le sondage.
- Implémentez le code qui permet à l'application d'exécuter les sondages.
- Redéployez la fonction Cloud.
Enregistrer la commande à barre oblique
Pour enregistrer une commande à barre oblique, revenez à la page de configuration de Chat dans la console (API et services > Tableau de bord > API Hangouts Chat > Configuration).
- Sous Commandes à barre oblique, cliquez sur Ajouter une commande à barre oblique.
- Dans le champ Nom, saisissez "/poll".
- Dans le champ ID de commande, saisissez "1".
- Dans Description, saisissez "Lancer un sondage".
- Sélectionnez Ouvre une boîte de dialogue.
- Cliquez sur OK.
- Cliquez sur Enregistrer.
L'application reconnaît désormais la commande /poll
et ouvre une boîte de dialogue. Configurez ensuite la boîte de dialogue.
Créer le formulaire de configuration en tant que boîte de dialogue
La commande à barre oblique ouvre une boîte de dialogue pour configurer le sujet du sondage et les choix possibles. Créez un fichier nommé config-form.js
à l'aide du contenu suivant :
/** 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;
Ce code génère le formulaire de boîte de dialogue qui permet à l'utilisateur de configurer le sondage. Il exporte également une constante pour le nombre maximal de choix possibles pour une question. Il est recommandé d'isoler la création du balisage de l'interface utilisateur dans des fonctions sans état, avec tout état transmis en tant que paramètre. Cela facilite la réutilisation, puis le rendu de cette fiche dans différents contextes.
Cette technique décompose également la fiche en unités ou composants plus petits. Bien que ce ne soit pas obligatoire, il est recommandé de l'utiliser, car elle est généralement plus lisible et facile à gérer lorsque vous créez des interfaces complexes.
Pour voir un exemple de code JSON complet créé, accédez à l'outil Card Builder.
Gérer la commande à barre oblique
Les commandes à barre oblique apparaissent sous forme d'événements MESSAGE
lorsqu'elles sont envoyées à l'application. Mettez à jour index.js
pour vérifier la présence d'une commande à barre oblique via un événement MESSAGE
et répondre avec une boîte de dialogue. Remplacez index.js
par ce qui suit :
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'application affiche désormais une boîte de dialogue lorsque la commande /poll
est appelée. Testez l'interaction en redéployant la fonction Cloud à partir de Cloud Shell.
gcloud functions deploy app --trigger-http --security-level=secure-always
Une fois la fonction Cloud déployée, envoyez un message à l'application avec la commande /poll
pour tester la commande à barre oblique et la boîte de dialogue. La boîte de dialogue envoie un événement CARD_CLICKED
avec l'action personnalisée start_poll
. L'événement est géré dans le point d'entrée mis à jour où il appelle la méthode startPoll
. Pour l'instant, la méthode startPoll
est bouchonnée uniquement pour fermer la boîte de dialogue. Dans la section suivante, vous allez implémenter la fonctionnalité de vote et connecter tous les éléments.
Implémenter la fiche de vote
Pour implémenter l'élément de vote de l'application, vous devez d'abord définir la fiche interactive qui fournit une interface permettant aux utilisateurs de voter.
Implémenter l'interface de vote
Créez un fichier nommé vote-card.js
avec le contenu suivant :
/**
* 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'implémentation ressemble à l'approche utilisée pour la boîte de dialogue, bien que le balisage pour les fiches interactives soit légèrement différent de celui des boîtes de dialogue. Comme précédemment, vous pouvez voir un exemple du code JSON généré dans l'outil Card Builder.
Implémenter l'action de votre
La fiche de vote inclut un bouton pour chaque choix. L'index de ce choix et l'état sérialisé du sondage sont associés au bouton. L'application reçoit un CARD_CLICKED
avec l'action vote
, ainsi que toutes les données associées au bouton comme paramètres.
Mettez index.js
à jour avec :
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],
}
}
La méthode recordVote
analyse l'état stocké et le met à jour avec le vote de l'utilisateur, puis affiche à nouveau la fiche. Les résultats du sondage sont sérialisés et stockés avec la fiche à chaque mise à jour.
Assembler les pièces
L'application est presque terminée. La commande à barre oblique étant implémentée avec le vote, il ne vous reste plus qu'à terminer la méthode startPoll
.
Avec toutefois un bémol.
Une fois la configuration du sondage envoyée, l'application doit effectuer deux actions :
- Fermer la boîte de dialogue
- Publier un nouveau message dans l'espace avec la fiche de vote
Malheureusement, la réponse directe à la requête HTTP ne peut effectuer qu'une seule de ces actions, et ce doit être la première. Pour publier la fiche de vote, l'application doit utiliser l'API Chat afin de créer un message de façon asynchrone.
Ajouter la bibliothèque cliente
Exécutez la commande suivante pour mettre à jour les dépendances de l'application de façon à inclure le client API Google pour Node.js.
npm install --save googleapis
Lancer le sondage
Mettez à jour index.js
vers la version finale ci-dessous :
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],
}
}
Redéployez la fonction :
gcloud functions deploy app --trigger-http --security-level=secure-always
Vous devriez maintenant pouvoir utiliser l'application dans son intégralité. Essayez d'appeler la commande /poll
, et fournissez une question et des choix. Après l'envoi, la fiche de sondage s'affiche.
Votez et voyez ce qu'il se passe.
Vous interroger vous-même n'a évidemment pas beaucoup d'intérêt. Invitez des amis et des collègues à répondre à votre sondage pour une meilleure expérience.
6. Félicitations
Félicitations ! Vous venez de créer et de déployer une application Google Chat à l'aide de Cloud Functions. Cet atelier de programmation a abordé de nombreux concepts fondamentaux de la création d'une application. Il reste toutefois beaucoup à découvrir. Consultez les ressources ci-dessous et n'oubliez pas de nettoyer votre projet pour éviter tous frais supplémentaires.
Autres activités
Si vous voulez étudier plus en détail la plate-forme Chat et cette application, nous vous suggérons les quelques activités suivantes :
- Que se passe-t-il lorsque vous utilisez @ pour mentionner l'application ? Essayez de mettre à jour l'application pour améliorer le comportement.
- La sérialisation de l'état du sondage dans la fiche fonctionne pour les petits espaces, mais présente des limites. Essayez de proposer une meilleure option.
- Que se passe-t-il si l'auteur veut modifier le sondage ou arrêter de recevoir de nouveaux votes ? Comment implémenteriez-vous ces fonctionnalités ?
- Le point de terminaison de l'application n'est pas encore sécurisé. Essayez d'ajouter une étape de validation pour vous assurer que les requêtes proviennent de Google Chat.
Ces activités ne sont que quelques idées pour améliorer l'application. Amusez-vous et faites appel à votre imagination.
Effectuer un nettoyage
Afin d'éviter la facturation sur votre compte Google Cloud Platform des ressources utilisées dans ce tutoriel, procédez comme suit :
- Dans la console Cloud, accédez à la page Gérer les ressources. En haut à gauche, cliquez sur Menu
> IAM et administration > Gérer les ressources.
- Dans la liste des projets, sélectionnez votre projet, puis cliquez sur Supprimer.
- Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.
En savoir plus
Pour en savoir plus sur le développement d'applications Chat, reportez-vous aux ressources suivantes :
- Documentation Google Chat pour les développeurs
- Commandes à barre oblique
- Boîtes de dialogue
- Fiches interactives
Pour plus d'informations sur le développement dans la console Google Cloud, reportez-vous aux ressources suivantes :