Catégories
Astuces et Design

Créez une application FAQ Slack avec les fonctions Netlify et FaunaDB

Parfois, lorsque vous cherchez une réponse rapide, il est vraiment utile d’avoir un système de FAQ en place, plutôt que d’attendre que quelqu'un réponde à une question. Ne serait-il pas formidable que Slack puisse simplement répondre à ces questions fréquentes pour nous? Dans ce didacticiel, nous allons faire exactement cela: une commande slash pour Slack qui répondra aux FAQ des utilisateurs. Nous stockerons nos réponses dans FaunaDB, utiliserons FQL pour rechercher la base de données et utiliserons une fonction Netlify pour fournir un point de terminaison sans serveur pour connecter Slack et FaunaDB.

Conditions préalables

Ce didacticiel suppose que vous avez les exigences suivantes:

  • Compte Github, utilisé pour se connecter à Netlify et Fauna, ainsi que pour stocker notre code
  • Espace de travail Slack avec autorisation de créer et d'installer de nouvelles applications
  • Node.js v12

Créer un package npm

Pour commencer, créez un nouveau dossier et initialisez un package npm en utilisant le gestionnaire de packages de votre choix et exécutez npm init -y de l'intérieur du dossier. Une fois le package créé, nous avons quelques packages npm à installer.

Exécutez ceci pour installer tous les packages dont nous aurons besoin pour ce tutoriel:

npm install express body-parser faunadb encoding serverless-http netlify-lambda

Ces packages sont expliqués ci-dessous, mais si vous les connaissez déjà, n'hésitez pas à les ignorer.

L'encodage a été installé en raison d'une erreur de plugin survenue dans @ netlify / plugin-functions-core au moment de l'écriture et peut ne pas être nécessaire lorsque vous suivez ce tutoriel.

Paquets

Express est un framework d'application web qui nous permettra de simplifier l'écriture de plusieurs endpoints pour notre fonction. Les fonctions Netlify nécessitent des gestionnaires pour chaque point de terminaison, mais express combiné avec serverless-http nous permettra d'écrire les points de terminaison en un seul endroit.

Body-parser est un middleware express qui prendra en charge le application/x-www-form-urlencoded les données que Slack enverra à notre fonction.

Faunadb est un module npm qui nous permet d'interagir avec la base de données via le pilote Javascript FaunaDB. Il nous permet de passer des requêtes de notre fonction à la base de données, afin d'obtenir les réponses

Serverless-http est un module qui enveloppe les applications Express au format attendu par les fonctions Netlify, ce qui signifie que nous n’aurons pas à réécrire notre code lorsque nous passerons du développement local à Netlify.

Netlify-lambda est un outil qui nous permettra de construire et de servir nos fonctions localement, de la même manière qu'elles seront construites et déployées sur Netlify. Cela signifie que nous pouvons développer localement avant de pousser notre code vers Netlify, augmentant ainsi la vitesse de notre flux de travail.

Créer une fonction

Une fois nos packages npm installés, il est temps de commencer à travailler sur la fonction. Nous utiliserons sans serveur pour encapsuler une application express, ce qui nous permettra de la déployer sur Netlify ultérieurement. Pour commencer, créez un fichier appelé netlify.tomlet ajoutez-y les éléments suivants:

(build)
  functions = "functions"

Nous utiliserons un fichier .gitignore, pour éviter que nos dossiers node_modules et functions ne soient ajoutés à git plus tard. Créez un fichier appelé .gitignore et ajoutez ce qui suit:

les fonctions/

node_modules /

Nous aurons également besoin d'un dossier appelé src, et d'un fichier à l'intérieur appelé server.js. Votre structure de fichier finale devrait ressembler à:

Avec cela en place, créez une application express de base en insérant le code ci-dessous dans server.js:

const express = require("express");
const bodyParser = require("body-parser");
const fauna = require("faunadb");
const serverless = require("serverless-http");
 
const app = express();
 
module.exports.handler = serverless(app);

Découvrez la dernière ligne; cela a l'air un peu différent d'une application express régulière. Plutôt que d’écouter sur un port, nous transmettons notre application au serveur sans serveur et l’utilisons comme gestionnaire, afin que Netlify puisse appeler notre fonction.

Configurons notre analyseur corporel à utiliser application/x-www-form-urlencoded données, ainsi que la mise en place d'un routeur. Ajoutez ce qui suit à server.js après avoir défini l'application:

const router = express.Router();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use("/.netlify/functions/server", router);

Notez que le routeur utilise /.netlify/functions/server comme point final. Ceci afin que Netlify puisse déployer correctement la fonction plus tard dans le didacticiel. Cela signifie que nous devrons l'ajouter à toutes les URL de base, afin d'appeler la fonction.

Créer une route de test

Avec une application de base en place, créons un itinéraire de test pour vérifier que tout fonctionne. Insérez le code suivant pour créer une route GET simple, qui renvoie un simple objet json:

router.get("/test", (req, res) => {
 res.json({ hello: "world" });
});

Avec cette route en place, activons notre fonction sur localhost et vérifions que nous obtenons une réponse. Nous utiliserons netlify-lambda pour servir notre application, afin de pouvoir imiter une fonction Netlify localement sur le port 9000. Dans notre package.json, ajoutez les lignes suivantes dans la section scripts:

"start": "./node_modules/.bin/netlify-lambda serve src",
   "build": "./node_modules/.bin/netlify-lambda build src"

Avec cela en place, après avoir enregistré le fichier, nous pouvons exécuter npm start pour commencer netlify-lambda sur le port 9000.

La commande build sera utilisée lors du déploiement ultérieur sur Netlify.

Une fois qu'il est opérationnel, nous pouvons visiter http://localhost:9000/.netlify/functions/server/test pour vérifier que notre fonction fonctionne comme prévu.

L'avantage de netlify-lambda est qu'il écoutera les modifications apportées à notre code, et se recompilera automatiquement chaque fois que nous mettons à jour quelque chose, afin que nous puissions le laisser fonctionner pendant la durée de ce tutoriel.

Démarrer l'URL ngrok

Nous avons maintenant une route de test fonctionnant sur notre machine locale, rendons-la disponible en ligne. Pour ce faire, nous utiliserons ngrok, un package npm qui fournit une URL publique pour notre fonction. Si vous n'avez pas encore installé ngrok, lancez d'abord npm install -g ngrok pour l'installer globalement sur votre machine. Puis cours ngrok http 9000 qui dirigera automatiquement le trafic vers notre fonction fonctionnant sur le port 9000.

Après avoir démarré ngrok, vous devriez voir une URL de transfert dans le terminal, que nous pouvons visiter pour confirmer que notre serveur est disponible en ligne. Copiez cette URL de base dans votre navigateur et suivez-la avec /.netlify/functions/server/test. Vous devriez voir le même résultat que lorsque nous avons effectué nos appels sur localhost, ce qui signifie que nous pouvons maintenant utiliser cette URL comme point de terminaison pour Slack!

Chaque fois que vous redémarrez ngrok, il crée une nouvelle URL, donc si vous devez l'arrêter à tout moment, vous devrez mettre à jour votre point de terminaison d'URL dans Slack.

Configurer Slack

Maintenant que nous avons une fonction en place, il est temps de passer à Slack pour créer l'application et la commande de barre oblique. Nous devrons déployer cette application dans notre espace de travail, ainsi que faire quelques mises à jour de notre code pour connecter notre fonction. Pour un ensemble d'instructions plus détaillées sur la création d'une nouvelle commande slash, vous pouvez suivre la documentation officielle de Slack. Pour un ensemble d'instructions simplifié, suivez ci-dessous:

Créer une nouvelle application Slack

Tout d'abord, créons notre nouvelle application Slack pour ces FAQ. Visitez https://api.slack.com/apps et sélectionnez Créer une nouvelle application pour commencer. Donnez un nom à votre application (j'ai utilisé la FAQ Fauna) et sélectionnez un espace de travail de développement pour l'application.

Créer une commande slash

Après avoir créé l'application, nous devons lui ajouter une commande barre oblique, afin de pouvoir interagir avec l'application. Sélectionnez les commandes de barre oblique dans le menu une fois l'application créée, puis créez une nouvelle commande. Remplissez le formulaire suivant avec le nom de votre commande (j'ai utilisé / faq) ainsi que l'URL de ngrok. N'oubliez pas d'ajouter /.netlify/functions/server/ jusqu'à la fin!

Installer l'application dans l'espace de travail

Une fois que vous avez créé votre commande slash, cliquez sur les informations de base dans la barre latérale à gauche pour revenir à la page principale de l'application. De là, sélectionnez le menu déroulant «Installer l'application sur votre espace de travail» et cliquez sur le bouton pour l'installer.

Une fois que vous avez autorisé l'accès, l'application sera installée et vous pourrez commencer à utiliser la commande barre oblique dans votre espace de travail.

Mettre à jour la fonction

Une fois notre nouvelle application en place, nous devrons créer un nouveau point de terminaison auquel Slack pourra envoyer les demandes. Pour cela, nous utiliserons le point de terminaison racine pour plus de simplicité. Le point de terminaison devra être en mesure de prendre une demande de publication avec application/x-www-form-urlencoded données, puis renvoyez une réponse d'état 200 avec un message. Pour ce faire, créons une nouvelle route de publication à la racine en ajoutant le code suivant à server.js:

router.post("/", async (req, res) => {
 
});

Maintenant que nous avons notre point de terminaison, nous pouvons également extraire et afficher le texte qui a été envoyé par slack en ajoutant la ligne suivante avant de définir le statut:

const text = req.body.text;
console.log(`Input text: ${text}`);

Pour l'instant, nous allons simplement transmettre ce texte dans la réponse et le renvoyer instantanément, pour nous assurer que l'application et la fonction Slack communiquent.

res.status(200);
res.send(text);

Maintenant, quand vous tapez / faq sur un canal slack, vous devriez récupérer le même message de la commande slack slash.

Formater la réponse

Plutôt que de simplement renvoyer du texte brut, nous pouvons utiliser le kit de blocs de Slack pour utiliser des éléments d'interface utilisateur spécialisés afin d'améliorer l'apparence de nos réponses. Si vous souhaitez créer une mise en page plus complexe, Slack propose un générateur Block Kit pour concevoir visuellement votre mise en page.

Pour l'instant, nous allons garder les choses simples et fournir simplement une réponse où chaque réponse est séparée par un séparateur. Ajoutez la fonction suivante à votre fichier server.js après la route de publication:

const format = (answers) => {
 if (answers.length == 0) {
   answers = ("No answers found");
 }
 
 let formatted = {
   blocks: (),
 };
 
 for (answer of answers) {
   formatted("blocks").push({
     type: "divider",
   });
   formatted("blocks").push({
     type: "section",
     text: {
       type: "mrkdwn",
       text: answer,
     },
   });
 }
 
 return formatted;
};

Avec cela en place, nous devons maintenant passer nos réponses dans cette fonction, pour formater les réponses avant de les renvoyer à Slack. Mettez à jour les éléments suivants dans la route de publication racine:

let answers = text;
const formattedAnswers = format(answers);

Maintenant, lorsque nous entrons la même commande dans l'application slash, nous devrions récupérer le même message, mais cette fois dans une version formatée!

Mise en place de la faune

Avec notre application slack en place et une fonction pour s'y connecter, nous devons maintenant commencer à travailler sur la base de données pour stocker nos réponses. Si vous n’avez jamais configuré de base de données avec FaunaDB auparavant, il existe une excellente documentation sur la façon de démarrer rapidement. Un bref aperçu étape par étape de la base de données et de la collection est inclus ci-dessous:

Créer une base de données

Tout d’abord, nous devons créer une nouvelle base de données. Après vous être connecté au tableau de bord Fauna en ligne, cliquez sur Nouvelle base de données. Donnez à votre nouvelle base de données un nom dont vous vous souviendrez (j'ai utilisé "slack-faq") et enregistrez la base de données.

Créer une collection

Avec cette base de données en place, nous avons maintenant besoin d'une collection. Cliquez sur le bouton «Nouvelle collection» qui devrait apparaître sur votre tableau de bord, et donnez un nom à votre collection (j'ai utilisé «faq»). Les jours d'historique et les valeurs TTL peuvent être conservés par défaut, mais vous devez vous assurer de ne pas ajouter de valeur au champ TTL, car nous ne voulons pas que nos documents soient supprimés automatiquement après un certain temps.

Ajouter des documents de questions / réponses

Maintenant que nous avons une base de données et une collection en place, nous pouvons commencer à y ajouter des documents. Chaque document doit suivre la structure:

{
   question: "a question string",
   answer: "an answer string",
   qTokens: (
       "first token",
       "second token",
       "third token"
   )
}

Les valeurs de qToken doivent être des termes clés dans la question, car nous les utiliserons pour une recherche tokenisée lorsque nous ne pouvons pas correspondre exactement à une question. Vous pouvez ajouter autant de qTokens que vous le souhaitez pour chaque question. Plus les jetons sont pertinents, plus les résultats seront précis. Par exemple, si notre question est «où sont les salles de bain», nous devrions inclure les qTokens «salle de bain», «salles de bains», «toilettes», «toilettes» et tout autre terme que vous pensez que les gens rechercheront lorsqu'ils essaieront de trouver des informations environ une salle de bain.

Les questions que j'ai utilisées pour développer une preuve de concept sont les suivantes:

{
  question: "where is the lobby",
  answer: "On the third floor",
  qTokens: ("lobby", "reception"),
},
{
  question: "when is payday",
  answer: "On the first Monday of each month",
  qTokens: ("payday", "pay", "paid"),
},
{
  question: "when is lunch",
  answer: "Lunch break is *12 - 1pm*",
  qTokens: ("lunch", "break", "eat"),
},
{
  question: "where are the bathrooms",
  answer: "Next to the elevators on each floor",
  qTokens: ("toilet", "bathroom", "toilets", "bathrooms"),
},
{
  question: "when are my breaks",
  answer: "You can take a break whenever you want",
  qTokens: ("break", "breaks"),
}

N'hésitez pas à prendre ce temps pour ajouter autant de documents que vous le souhaitez et autant de qTokens que vous pensez que chaque question a besoin, puis nous passerons à l'étape suivante.

Créer des index

Une fois ces questions en place, nous créerons deux index pour nous permettre de rechercher dans la base de données. Commencez par créer un index appelé «answers_by_question», en sélectionnant question comme terme et réponse comme valeur. Cela nous permettra de rechercher toutes les réponses par leur question associée.

Ensuite, créez un index appelé «answers_by_qTokens», en sélectionnant qTokens comme terme et answer comme valeur. Nous utiliserons cet index pour nous permettre de rechercher dans les qTokens de tous les éléments de la base de données.

Recherche dans la base de données

Pour lancer une recherche dans notre base de données, nous allons faire deux choses. Tout d'abord, nous allons lancer une recherche pour une correspondance exacte à la question, afin de pouvoir fournir une réponse unique à l'utilisateur. Deuxièmement, si cette recherche ne trouve pas de résultat, nous effectuerons une recherche sur les qTokens de chaque réponse, en retournant tous les résultats qui fournissent une correspondance. Nous utiliserons le shell en ligne de Fauna pour démontrer et expliquer ces requêtes, avant de les utiliser dans notre fonction.

Correspondance exacte

Avant de rechercher les jetons, nous allons tester si nous pouvons faire correspondre exactement la question d'entrée, car cela permettra d'obtenir la meilleure réponse à ce que l'utilisateur a demandé. Pour rechercher nos questions, nous allons comparer avec l'index «answers_by_question», puis paginer nos réponses. Copiez le code suivant dans le shell Fauna en ligne pour voir cela en action:

q.Paginate(q.Match(q.Index("answers_by_question"), "where is the lobby"))

Si vous avez une question correspondant à l'exemple «où est le lobby» ci-dessus, vous devriez voir la réponse attendue «Au troisième étage» en conséquence.

Recherche des jetons

Pour les cas où il n'y a pas de correspondance exacte dans la base de données, nous devrons utiliser nos qTokens pour trouver les réponses pertinentes. Pour cela, nous allons comparer avec l'index «answers_by_qTokens» que nous avons créé et paginer à nouveau nos réponses. Copiez ce qui suit dans le shell en ligne pour voir comment cela fonctionne:

q.Paginate(q.Match(q.Index("answers_by_qTokens"), "break"))

Si vous avez des questions avec la «pause» qToken des exemples de questions, vous devriez voir toutes les réponses renvoyées en conséquence.

Connecter la fonction à Fauna

Nous avons nos recherches déterminées, mais actuellement, nous ne pouvons les exécuter qu'à partir du shell en ligne. Pour les utiliser dans notre fonction, une configuration est requise, ainsi qu'une mise à jour du code de notre fonction.

Configuration des fonctions

Pour nous connecter à Fauna depuis notre fonction, nous devrons créer une clé de serveur. Dans le tableau de bord de votre base de données, sélectionnez la sécurité dans la barre latérale gauche et créez une nouvelle clé. Donnez à votre nouvelle clé un nom que vous reconnaîtrez et assurez-vous que le menu déroulant a sélectionné Serveur, pas Admin. Enfin, une fois la clé créée, ajoutez le code suivant à server.js avant la route de test, en remplaçant le valeur avec le secret fourni par Fauna.

const q = fauna.query;
const client = new fauna.Client({
 secret: "",
});

Il serait préférable de stocker cette clé dans une variable d'environnement dans Netlify, plutôt que directement dans le code, mais cela sort du cadre de ce tutoriel. Si vous souhaitez utiliser des variables d'environnement, ce post Netlify explique comment le faire.

Mettre à jour le code de fonction

Pour inclure nos nouvelles requêtes de recherche dans la fonction, copiez le code suivant dans server.js après l'itinéraire post:

const searchText = async (text) => {
 console.log("Beginning searchText");
 const answer = await client.query(
   q.Paginate(q.Match(q.Index("answers_by_question"), text))
 );
 console.log(`searchText response: ${answer.data}`);
 return answer.data;
};
 
const getTokenResponse = async (text) => {
 console.log("Beginning getTokenResponse");
 let answers = ();
 const questionTokens = text.split(/( )+/);
 console.log(`Tokens: ${questionTokens}`);
 for (token of questionTokens) {
   const tokenResponse = await client.query(
     q.Paginate(q.Match(q.Index("answers_by_qTokens"), text))
   );
   answers = (...answers, ...tokenResponse.data);
 }
 console.log(`Token answers: ${answers}`);
 return answers;
};

Ces fonctions répliquent les mêmes fonctionnalités que les requêtes que nous avons précédemment exécutées dans le shell Fauna en ligne, mais nous pouvons maintenant les utiliser à partir de notre fonction.

Déployer sur Netlify

Maintenant, la fonction recherche la base de données, il ne reste plus qu'à la mettre sur le cloud plutôt que sur une machine locale. Pour ce faire, nous utiliserons une fonction Netlify déployée à partir d'un référentiel GitHub.

Tout d'abord, ajoutez un nouveau dépôt sur Github et transférez-y votre code. Une fois le code là, allez sur Netlify et inscrivez-vous ou connectez-vous en utilisant votre profil Github. Sur la page d'accueil de Netlify, sélectionnez "Nouveau site de git" pour déployer un nouveau site à l'aide du dépôt que vous venez de créer dans Github.

Si vous n'avez jamais déployé de site dans Netlify auparavant, cet article explique le processus de déploiement à partir de git.

Lorsque vous créez le nouveau site, assurez-vous que votre commande build est définie sur npm run build, pour que Netlify génère la fonction avant le déploiement. Le répertoire de publication peut être laissé vide, car nous déployons uniquement une fonction, plutôt que des pages.

Netlify va maintenant créer et déployer votre dépôt, générant une URL unique pour le déploiement du site. Nous pouvons utiliser cette URL de base pour accéder au point de terminaison de test de notre fonction plus tôt, pour nous assurer que les choses fonctionnent.

La dernière chose à faire est de mettre à jour le point de terminaison Slack vers notre nouvelle URL! Accédez à votre application, puis sélectionnez "commandes barre oblique" dans la barre latérale gauche. Cliquez sur l'icône en forme de crayon pour modifier la commande barre oblique et collez la nouvelle URL de la fonction. Enfin, vous pouvez utiliser votre nouvelle commande slash dans tous les canaux Slack autorisés!

Conclusion

Voilà, une commande slack fonctionnelle entièrement sans serveur. Nous avons utilisé FaunaDB pour stocker nos réponses et nous y sommes connectés via une fonction Netlify. De plus, en utilisant Express, nous avons la possibilité d'ajouter d'autres points de terminaison à la fonction pour ajouter de nouvelles questions, ou tout ce que vous pouvez imaginer pour étendre davantage ce projet! J'espère que maintenant, au lieu d'attendre que quelqu'un réponde à vos questions, vous pouvez simplement utiliser / faq et obtenir la réponse instantanément!


Matthew Williams est un ingénieur logiciel de Melbourne, en Australie, qui croit que l'avenir de la technologie est sans serveur. Si vous souhaitez en savoir plus sur lui, consultez ses articles Medium ou ses dépôts GitHub.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *